NeoMutt  2024-04-25-89-g194907
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, sizeof(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, int max_cols, 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, int max_cols, 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, int max_cols, 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, int max_cols, 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, int max_cols, 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, int max_cols, 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, int max_cols, 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, int max_cols, 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, sizeof(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(char *prompt, KeyFlags abilities, bool only_public_key)
687{
688 struct SmimeKey *key = NULL;
689 struct Buffer *resp = buf_pool_get();
690
691 if (!prompt)
692 prompt = _("Enter keyID: ");
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 char buf[256] = { 0 };
732 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), mailbox);
733 key = smime_ask_for_key(buf, KEYFLAG_CANENCRYPT, false);
734 }
735
736 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
737 size_t smime_keys_len = mutt_str_len(c_smime_keys);
738
739 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
740 k = key ? key->hash : NONULL(c_smime_default_key);
741
742 /* if the key is different from last time */
743 if ((buf_len(&SmimeKeyToUse) <= smime_keys_len) ||
744 !mutt_istr_equal(k, SmimeKeyToUse.data + smime_keys_len + 1))
745 {
747 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), k);
748 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
749 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), k);
750 }
751
752 smime_key_free(&key);
753}
754
759{
760 const bool c_smime_decrypt_use_default_key = cs_subset_bool(NeoMutt->sub, "smime_decrypt_use_default_key");
761 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
762 if (c_smime_decrypt_use_default_key && c_smime_default_key)
763 {
764 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
765 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), c_smime_default_key);
766 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
767 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), c_smime_default_key);
768 return;
769 }
770
771 struct Address *a = NULL;
772 TAILQ_FOREACH(a, &env->to, entries)
773 {
774 if (mutt_addr_is_user(a))
775 {
777 return;
778 }
779 }
780
781 TAILQ_FOREACH(a, &env->cc, entries)
782 {
783 if (mutt_addr_is_user(a))
784 {
786 return;
787 }
788 }
789
790 struct Address *f = mutt_default_from(NeoMutt->sub);
792 mutt_addr_free(&f);
793}
794
798char *smime_class_find_keys(const struct AddressList *al, bool oppenc_mode)
799{
800 struct SmimeKey *key = NULL;
801 char *keyid = NULL, *keylist = NULL;
802 size_t keylist_size = 0;
803 size_t keylist_used = 0;
804
805 struct Address *a = NULL;
806 TAILQ_FOREACH(a, al, entries)
807 {
808 key = smime_get_key_by_addr(buf_string(a->mailbox), KEYFLAG_CANENCRYPT, true, oppenc_mode);
809 if (!key && !oppenc_mode && isatty(STDIN_FILENO))
810 {
811 char buf[1024] = { 0 };
812 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), buf_string(a->mailbox));
813 key = smime_ask_for_key(buf, KEYFLAG_CANENCRYPT, true);
814 }
815 if (!key)
816 {
817 if (!oppenc_mode)
818 mutt_message(_("No (valid) certificate found for %s"), buf_string(a->mailbox));
819 FREE(&keylist);
820 return NULL;
821 }
822
823 keyid = key->hash;
824 keylist_size += mutt_str_len(keyid) + 2;
825 mutt_mem_realloc(&keylist, keylist_size);
826 sprintf(keylist + keylist_used, "%s%s", keylist_used ? " " : "", keyid);
827 keylist_used = mutt_str_len(keylist);
828
829 smime_key_free(&key);
830 }
831 return keylist;
832}
833
845static int smime_handle_cert_email(const char *certificate, const char *mailbox,
846 bool copy, char ***buffer, int *num)
847{
848 char email[256] = { 0 };
849 int rc = -1, count = 0;
850 pid_t pid;
851
852 FILE *fp_err = mutt_file_mkstemp();
853 if (!fp_err)
854 {
855 mutt_perror(_("Can't create temporary file"));
856 return 1;
857 }
858
859 FILE *fp_out = mutt_file_mkstemp();
860 if (!fp_out)
861 {
862 mutt_file_fclose(&fp_err);
863 mutt_perror(_("Can't create temporary file"));
864 return 1;
865 }
866
867 const struct Expando *c_smime_get_cert_email_command =
868 cs_subset_expando(NeoMutt->sub, "smime_get_cert_email_command");
869 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_out), fileno(fp_err), certificate,
870 NULL, NULL, NULL, NULL, NULL, NULL, c_smime_get_cert_email_command);
871 if (pid == -1)
872 {
873 mutt_message(_("Error: unable to create OpenSSL subprocess"));
874 mutt_file_fclose(&fp_err);
875 mutt_file_fclose(&fp_out);
876 return 1;
877 }
878
879 filter_wait(pid);
880
881 fflush(fp_out);
882 rewind(fp_out);
883 fflush(fp_err);
884 rewind(fp_err);
885
886 while ((fgets(email, sizeof(email), fp_out)))
887 {
888 size_t len = mutt_str_len(email);
889 if (len && (email[len - 1] == '\n'))
890 email[len - 1] = '\0';
891 if (mutt_istr_startswith(email, mailbox))
892 rc = 1;
893
894 rc = (rc < 0) ? 0 : rc;
895 count++;
896 }
897
898 if (rc == -1)
899 {
900 mutt_endwin();
901 mutt_file_copy_stream(fp_err, stdout);
902 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
903 rc = 1;
904 }
905 else if (rc == 0)
906 {
907 rc = 1;
908 }
909 else
910 {
911 rc = 0;
912 }
913
914 if (copy && buffer && num)
915 {
916 (*num) = count;
917 *buffer = mutt_mem_calloc(count, sizeof(char *));
918 count = 0;
919
920 rewind(fp_out);
921 while ((fgets(email, sizeof(email), fp_out)))
922 {
923 size_t len = mutt_str_len(email);
924 if (len && (email[len - 1] == '\n'))
925 email[len - 1] = '\0';
926 (*buffer)[count] = mutt_mem_calloc(mutt_str_len(email) + 1, sizeof(char));
927 strncpy((*buffer)[count], email, mutt_str_len(email));
928 count++;
929 }
930 }
931 else if (copy)
932 {
933 rc = 2;
934 }
935
936 mutt_file_fclose(&fp_out);
937 mutt_file_fclose(&fp_err);
938
939 return rc;
940}
941
947static char *smime_extract_certificate(const char *infile)
948{
949 FILE *fp_err = NULL;
950 FILE *fp_out = NULL;
951 FILE *fp_cert = NULL;
952 char *rc = NULL;
953 pid_t pid;
954 int empty;
955
956 struct Buffer *pk7out = buf_pool_get();
957 struct Buffer *certfile = buf_pool_get();
958
959 fp_err = mutt_file_mkstemp();
960 if (!fp_err)
961 {
962 mutt_perror(_("Can't create temporary file"));
963 goto cleanup;
964 }
965
966 buf_mktemp(pk7out);
967 fp_out = mutt_file_fopen(buf_string(pk7out), "w+");
968 if (!fp_out)
969 {
970 mutt_perror("%s", buf_string(pk7out));
971 goto cleanup;
972 }
973
974 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
975 * extract the full set of certificates directly. */
976 const struct Expando *c_smime_pk7out_command = cs_subset_expando(NeoMutt->sub, "smime_pk7out_command");
977 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_out), fileno(fp_err), infile,
978 NULL, NULL, NULL, NULL, NULL, NULL, c_smime_pk7out_command);
979 if (pid == -1)
980 {
981 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
982 goto cleanup;
983 }
984
985 filter_wait(pid);
986
987 fflush(fp_out);
988 rewind(fp_out);
989 fflush(fp_err);
990 rewind(fp_err);
991 empty = (fgetc(fp_out) == EOF);
992 if (empty)
993 {
994 mutt_perror("%s", buf_string(pk7out));
995 mutt_file_copy_stream(fp_err, stdout);
996 goto cleanup;
997 }
998 mutt_file_fclose(&fp_out);
999
1000 buf_mktemp(certfile);
1001 fp_cert = mutt_file_fopen(buf_string(certfile), "w+");
1002 if (!fp_cert)
1003 {
1004 mutt_perror("%s", buf_string(certfile));
1006 goto cleanup;
1007 }
1008
1009 // Step 2: Extract the certificates from a PKCS#7 structure.
1010 const struct Expando *c_smime_get_cert_command = cs_subset_expando(NeoMutt->sub, "smime_get_cert_command");
1011 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_cert), fileno(fp_err),
1012 buf_string(pk7out), NULL, NULL, NULL, NULL, NULL, NULL,
1013 c_smime_get_cert_command);
1014 if (pid == -1)
1015 {
1016 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
1018 goto cleanup;
1019 }
1020
1021 filter_wait(pid);
1022
1024
1025 fflush(fp_cert);
1026 rewind(fp_cert);
1027 fflush(fp_err);
1028 rewind(fp_err);
1029 empty = (fgetc(fp_cert) == EOF);
1030 if (empty)
1031 {
1032 mutt_file_copy_stream(fp_err, stdout);
1033 goto cleanup;
1034 }
1035
1036 mutt_file_fclose(&fp_cert);
1037
1038 rc = buf_strdup(certfile);
1039
1040cleanup:
1041 mutt_file_fclose(&fp_err);
1042 if (fp_out)
1043 {
1044 mutt_file_fclose(&fp_out);
1046 }
1047 if (fp_cert)
1048 {
1049 mutt_file_fclose(&fp_cert);
1050 mutt_file_unlink(buf_string(certfile));
1051 }
1052 buf_pool_release(&pk7out);
1053 buf_pool_release(&certfile);
1054 return rc;
1055}
1056
1062static char *smime_extract_signer_certificate(const char *infile)
1063{
1064 char *cert = NULL;
1065 struct Buffer *certfile = NULL;
1066 pid_t pid;
1067 int empty;
1068
1069 FILE *fp_err = mutt_file_mkstemp();
1070 if (!fp_err)
1071 {
1072 mutt_perror(_("Can't create temporary file"));
1073 return NULL;
1074 }
1075
1076 certfile = buf_pool_get();
1077 buf_mktemp(certfile);
1078 FILE *fp_out = mutt_file_fopen(buf_string(certfile), "w+");
1079 if (!fp_out)
1080 {
1081 mutt_file_fclose(&fp_err);
1082 mutt_perror("%s", buf_string(certfile));
1083 goto cleanup;
1084 }
1085
1086 /* Extract signer's certificate
1087 */
1088 const struct Expando *c_smime_get_signer_cert_command =
1089 cs_subset_expando(NeoMutt->sub, "smime_get_signer_cert_command");
1090 pid = smime_invoke(NULL, NULL, NULL, -1, -1, fileno(fp_err), infile, NULL, NULL, NULL,
1091 NULL, buf_string(certfile), NULL, c_smime_get_signer_cert_command);
1092 if (pid == -1)
1093 {
1094 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
1095 goto cleanup;
1096 }
1097
1098 filter_wait(pid);
1099
1100 fflush(fp_out);
1101 rewind(fp_out);
1102 fflush(fp_err);
1103 rewind(fp_err);
1104 empty = (fgetc(fp_out) == EOF);
1105 if (empty)
1106 {
1107 mutt_endwin();
1108 mutt_file_copy_stream(fp_err, stdout);
1110 goto cleanup;
1111 }
1112
1113 mutt_file_fclose(&fp_out);
1114 cert = buf_strdup(certfile);
1115
1116cleanup:
1117 mutt_file_fclose(&fp_err);
1118 if (fp_out)
1119 {
1120 mutt_file_fclose(&fp_out);
1121 mutt_file_unlink(buf_string(certfile));
1122 }
1123 buf_pool_release(&certfile);
1124 return cert;
1125}
1126
1130void smime_class_invoke_import(const char *infile, const char *mailbox)
1131{
1132 char *certfile = NULL;
1133 struct Buffer *buf = NULL;
1134
1135 FILE *fp_out = NULL;
1136 FILE *fp_err = mutt_file_mkstemp();
1137 if (!fp_err)
1138 {
1139 mutt_perror(_("Can't create temporary file"));
1140 goto done;
1141 }
1142
1143 fp_out = mutt_file_mkstemp();
1144 if (!fp_out)
1145 {
1146 mutt_perror(_("Can't create temporary file"));
1147 goto done;
1148 }
1149
1150 buf = buf_pool_get();
1151 const bool c_smime_ask_cert_label = cs_subset_bool(NeoMutt->sub, "smime_ask_cert_label");
1152 if (c_smime_ask_cert_label)
1153 {
1154 if ((mw_get_field(_("Label for certificate: "), buf, MUTT_COMP_NO_FLAGS,
1155 HC_OTHER, NULL, NULL) != 0) ||
1156 buf_is_empty(buf))
1157 {
1158 goto done;
1159 }
1160 }
1161
1162 mutt_endwin();
1163 certfile = smime_extract_certificate(infile);
1164 if (certfile)
1165 {
1166 mutt_endwin();
1167
1168 const struct Expando *c_smime_import_cert_command =
1169 cs_subset_expando(NeoMutt->sub, "smime_import_cert_command");
1170 FILE *fp_smime_in = NULL;
1171 pid_t pid = smime_invoke(&fp_smime_in, NULL, NULL, -1, fileno(fp_out),
1172 fileno(fp_err), certfile, NULL, NULL, NULL, NULL,
1173 NULL, NULL, c_smime_import_cert_command);
1174 if (pid == -1)
1175 {
1176 mutt_message(_("Error: unable to create OpenSSL subprocess"));
1177 goto done;
1178 }
1179 fputs(buf_string(buf), fp_smime_in);
1180 fputc('\n', fp_smime_in);
1181 mutt_file_fclose(&fp_smime_in);
1182
1183 filter_wait(pid);
1184
1185 mutt_file_unlink(certfile);
1186 FREE(&certfile);
1187 }
1188
1189 fflush(fp_out);
1190 rewind(fp_out);
1191 fflush(fp_err);
1192 rewind(fp_err);
1193
1194 mutt_file_copy_stream(fp_out, stdout);
1195 mutt_file_copy_stream(fp_err, stdout);
1196
1197done:
1198 mutt_file_fclose(&fp_out);
1199 mutt_file_fclose(&fp_err);
1200 buf_pool_release(&buf);
1201}
1202
1206int smime_class_verify_sender(struct Email *e, struct Message *msg)
1207{
1208 const char *mbox = NULL, *certfile = NULL;
1209 int rc = 1;
1210
1211 struct Buffer *tempfname = buf_pool_get();
1212 buf_mktemp(tempfname);
1213 FILE *fp_out = mutt_file_fopen(buf_string(tempfname), "w");
1214 if (!fp_out)
1215 {
1216 mutt_perror("%s", buf_string(tempfname));
1217 goto cleanup;
1218 }
1219
1220 const bool encrypt = e->security & SEC_ENCRYPT;
1221 mutt_copy_message(fp_out, e, msg,
1223 encrypt ? (CH_MIME | CH_WEED | CH_NONEWLINE) : CH_NO_FLAGS, 0);
1224
1225 fflush(fp_out);
1226 mutt_file_fclose(&fp_out);
1227
1228 if (!TAILQ_EMPTY(&e->env->from))
1229 {
1231 mbox = buf_string(TAILQ_FIRST(&e->env->from)->mailbox);
1232 }
1233 else if (!TAILQ_EMPTY(&e->env->sender))
1234 {
1236 mbox = buf_string(TAILQ_FIRST(&e->env->sender)->mailbox);
1237 }
1238
1239 if (mbox)
1240 {
1241 certfile = smime_extract_signer_certificate(buf_string(tempfname));
1242 if (certfile)
1243 {
1244 mutt_file_unlink(buf_string(tempfname));
1245 if (smime_handle_cert_email(certfile, mbox, false, NULL, NULL))
1246 {
1247 if (isendwin())
1249 }
1250 else
1251 {
1252 rc = 0;
1253 }
1254 mutt_file_unlink(certfile);
1255 FREE(&certfile);
1256 }
1257 else
1258 {
1259 mutt_any_key_to_continue(_("no certfile"));
1260 }
1261 }
1262 else
1263 {
1264 mutt_any_key_to_continue(_("no mbox"));
1265 }
1266
1267 mutt_file_unlink(buf_string(tempfname));
1268
1269cleanup:
1270 buf_pool_release(&tempfname);
1271 return rc;
1272}
1273
1290static pid_t smime_invoke_encrypt(FILE **fp_smime_in, FILE **fp_smime_out,
1291 FILE **fp_smime_err, int fp_smime_infd,
1292 int fp_smime_outfd, int fp_smime_errfd,
1293 const char *fname, const char *uids)
1294{
1295 const char *const c_smime_encrypt_with = cs_subset_string(NeoMutt->sub, "smime_encrypt_with");
1296 const struct Expando *c_smime_encrypt_command = cs_subset_expando(NeoMutt->sub, "smime_encrypt_command");
1297 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd,
1298 fp_smime_outfd, fp_smime_errfd, fname, NULL, c_smime_encrypt_with,
1299 NULL, NULL, uids, NULL, c_smime_encrypt_command);
1300}
1301
1317static pid_t smime_invoke_sign(FILE **fp_smime_in, FILE **fp_smime_out,
1318 FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd,
1319 int fp_smime_errfd, const char *fname)
1320{
1321 const char *const c_smime_sign_digest_alg = cs_subset_string(NeoMutt->sub, "smime_sign_digest_alg");
1322 const struct Expando *c_smime_sign_command = cs_subset_expando(NeoMutt->sub, "smime_sign_command");
1323 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd, fp_smime_outfd,
1324 fp_smime_errfd, fname, NULL, NULL, c_smime_sign_digest_alg,
1326 buf_string(&SmimeIntermediateToUse), c_smime_sign_command);
1327}
1328
1332struct Body *smime_class_build_smime_entity(struct Body *b, char *certlist)
1333{
1334 char buf[1024] = { 0 };
1335 char certfile[PATH_MAX] = { 0 };
1336 char *cert_end = NULL;
1337 FILE *fp_smime_in = NULL, *fp_smime_err = NULL, *fp_out = NULL, *fp_tmp = NULL;
1338 struct Body *b_enc = NULL;
1339 bool err = false;
1340 int empty, off;
1341 pid_t pid;
1342
1343 struct Buffer *tempfile = buf_pool_get();
1344 struct Buffer *smime_infile = buf_pool_get();
1345
1346 buf_mktemp(tempfile);
1347 fp_out = mutt_file_fopen(buf_string(tempfile), "w+");
1348 if (!fp_out)
1349 {
1350 mutt_perror("%s", buf_string(tempfile));
1351 goto cleanup;
1352 }
1353
1354 fp_smime_err = mutt_file_mkstemp();
1355 if (!fp_smime_err)
1356 {
1357 mutt_perror(_("Can't create temporary file"));
1358 goto cleanup;
1359 }
1360
1361 buf_mktemp(smime_infile);
1362 fp_tmp = mutt_file_fopen(buf_string(smime_infile), "w+");
1363 if (!fp_tmp)
1364 {
1365 mutt_perror("%s", buf_string(smime_infile));
1366 goto cleanup;
1367 }
1368
1369 *certfile = '\0';
1370 for (char *cert_start = certlist; cert_start; cert_start = cert_end)
1371 {
1372 cert_end = strchr(cert_start, ' ');
1373 if (cert_end)
1374 *cert_end = '\0';
1375 if (*cert_start)
1376 {
1377 off = mutt_str_len(certfile);
1378 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
1379 snprintf(certfile + off, sizeof(certfile) - off, "%s%s/%s",
1380 (off != 0) ? " " : "", NONULL(c_smime_certificates), cert_start);
1381 }
1382 if (cert_end)
1383 *cert_end++ = ' ';
1384 }
1385
1386 /* write a MIME entity */
1387 mutt_write_mime_header(b, fp_tmp, NeoMutt->sub);
1388 fputc('\n', fp_tmp);
1389 mutt_write_mime_body(b, fp_tmp, NeoMutt->sub);
1390 mutt_file_fclose(&fp_tmp);
1391
1392 pid = smime_invoke_encrypt(&fp_smime_in, NULL, NULL, -1, fileno(fp_out),
1393 fileno(fp_smime_err), buf_string(smime_infile), certfile);
1394 if (pid == -1)
1395 {
1396 mutt_file_unlink(buf_string(smime_infile));
1397 goto cleanup;
1398 }
1399
1400 mutt_file_fclose(&fp_smime_in);
1401
1402 filter_wait(pid);
1403 mutt_file_unlink(buf_string(smime_infile));
1404
1405 fflush(fp_out);
1406 rewind(fp_out);
1407 empty = (fgetc(fp_out) == EOF);
1408 mutt_file_fclose(&fp_out);
1409
1410 fflush(fp_smime_err);
1411 rewind(fp_smime_err);
1412 while (fgets(buf, sizeof(buf) - 1, fp_smime_err))
1413 {
1414 err = true;
1415 fputs(buf, stdout);
1416 }
1417 mutt_file_fclose(&fp_smime_err);
1418
1419 /* pause if there is any error output from SMIME */
1420 if (err)
1422
1423 if (empty)
1424 {
1425 /* fatal error while trying to encrypt message */
1426 if (!err)
1427 mutt_any_key_to_continue(_("No output from OpenSSL..."));
1428 mutt_file_unlink(buf_string(tempfile));
1429 goto cleanup;
1430 }
1431
1432 b_enc = mutt_body_new();
1433 b_enc->type = TYPE_APPLICATION;
1434 b_enc->subtype = mutt_str_dup("pkcs7-mime");
1435 mutt_param_set(&b_enc->parameter, "name", "smime.p7m");
1436 mutt_param_set(&b_enc->parameter, "smime-type", "enveloped-data");
1437 b_enc->encoding = ENC_BASE64; /* The output of OpenSSL SHOULD be binary */
1438 b_enc->use_disp = true;
1439 b_enc->disposition = DISP_ATTACH;
1440 b_enc->d_filename = mutt_str_dup("smime.p7m");
1441 b_enc->filename = buf_strdup(tempfile);
1442 b_enc->unlink = true; /* delete after sending the message */
1443 b_enc->parts = NULL;
1444 b_enc->next = NULL;
1445
1446cleanup:
1447 if (fp_out)
1448 {
1449 mutt_file_fclose(&fp_out);
1450 mutt_file_unlink(buf_string(tempfile));
1451 }
1452 mutt_file_fclose(&fp_smime_err);
1453 if (fp_tmp)
1454 {
1455 mutt_file_fclose(&fp_tmp);
1456 mutt_file_unlink(buf_string(smime_infile));
1457 }
1458 buf_pool_release(&tempfile);
1459 buf_pool_release(&smime_infile);
1460
1461 return b_enc;
1462}
1463
1476static char *openssl_md_to_smime_micalg(const char *md)
1477{
1478 if (!md)
1479 return NULL;
1480
1481 char *micalg = NULL;
1482 if (mutt_istr_startswith(md, "sha"))
1483 {
1484 mutt_str_asprintf(&micalg, "sha-%s", md + 3);
1485 }
1486 else
1487 {
1488 micalg = mutt_str_dup(md);
1489 }
1490
1491 return micalg;
1492}
1493
1497struct Body *smime_class_sign_message(struct Body *b, const struct AddressList *from)
1498{
1499 struct Body *b_sign = NULL;
1500 struct Body *rc = NULL;
1501 char buf[1024] = { 0 };
1502 struct Buffer *filetosign = NULL, *signedfile = NULL;
1503 FILE *fp_smime_in = NULL, *fp_smime_out = NULL, *fp_smime_err = NULL, *fp_sign = NULL;
1504 bool err = false;
1505 int empty = 0;
1506 pid_t pid;
1507 const char *intermediates = NULL;
1508
1509 const char *const c_smime_sign_as = cs_subset_string(NeoMutt->sub, "smime_sign_as");
1510 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
1511 const char *signas = c_smime_sign_as ? c_smime_sign_as : c_smime_default_key;
1512 if (!signas || (*signas == '\0'))
1513 {
1514 mutt_error(_("Can't sign: No key specified. Use Sign As."));
1515 return NULL;
1516 }
1517
1518 crypt_convert_to_7bit(b); /* Signed data _must_ be in 7-bit format. */
1519
1520 filetosign = buf_pool_get();
1521 signedfile = buf_pool_get();
1522
1523 buf_mktemp(filetosign);
1524 fp_sign = mutt_file_fopen(buf_string(filetosign), "w+");
1525 if (!fp_sign)
1526 {
1527 mutt_perror("%s", buf_string(filetosign));
1528 goto cleanup;
1529 }
1530
1531 buf_mktemp(signedfile);
1532 fp_smime_out = mutt_file_fopen(buf_string(signedfile), "w+");
1533 if (!fp_smime_out)
1534 {
1535 mutt_perror("%s", buf_string(signedfile));
1536 goto cleanup;
1537 }
1538
1539 mutt_write_mime_header(b, fp_sign, NeoMutt->sub);
1540 fputc('\n', fp_sign);
1541 mutt_write_mime_body(b, fp_sign, NeoMutt->sub);
1542 mutt_file_fclose(&fp_sign);
1543
1544 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
1545 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
1546 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), signas);
1547 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), signas);
1548
1549 struct SmimeKey *signas_key = smime_get_key_by_hash(signas, 1);
1550 if (!signas_key || mutt_str_equal("?", signas_key->issuer))
1551 intermediates = signas; /* so openssl won't complain in any case */
1552 else
1553 intermediates = signas_key->issuer;
1554
1555 buf_printf(&SmimeIntermediateToUse, "%s/%s", NONULL(c_smime_certificates), intermediates);
1556
1557 smime_key_free(&signas_key);
1558
1559 pid = smime_invoke_sign(&fp_smime_in, NULL, &fp_smime_err, -1,
1560 fileno(fp_smime_out), -1, buf_string(filetosign));
1561 if (pid == -1)
1562 {
1563 mutt_perror(_("Can't open OpenSSL subprocess"));
1564 mutt_file_unlink(buf_string(filetosign));
1565 goto cleanup;
1566 }
1567 fputs(SmimePass, fp_smime_in);
1568 fputc('\n', fp_smime_in);
1569 mutt_file_fclose(&fp_smime_in);
1570
1571 filter_wait(pid);
1572
1573 /* check for errors from OpenSSL */
1574 err = false;
1575 fflush(fp_smime_err);
1576 rewind(fp_smime_err);
1577 while (fgets(buf, sizeof(buf) - 1, fp_smime_err))
1578 {
1579 err = true;
1580 fputs(buf, stdout);
1581 }
1582 mutt_file_fclose(&fp_smime_err);
1583
1584 fflush(fp_smime_out);
1585 rewind(fp_smime_out);
1586 empty = (fgetc(fp_smime_out) == EOF);
1587 mutt_file_fclose(&fp_smime_out);
1588
1589 mutt_file_unlink(buf_string(filetosign));
1590
1591 if (err)
1593
1594 if (empty)
1595 {
1596 mutt_any_key_to_continue(_("No output from OpenSSL..."));
1597 mutt_file_unlink(buf_string(signedfile));
1598 goto cleanup; /* fatal error while signing */
1599 }
1600
1601 b_sign = mutt_body_new();
1602 b_sign->type = TYPE_MULTIPART;
1603 b_sign->subtype = mutt_str_dup("signed");
1604 b_sign->encoding = ENC_7BIT;
1605 b_sign->use_disp = false;
1606 b_sign->disposition = DISP_INLINE;
1607
1609
1610 const char *const c_smime_sign_digest_alg = cs_subset_string(NeoMutt->sub, "smime_sign_digest_alg");
1611 char *micalg = openssl_md_to_smime_micalg(c_smime_sign_digest_alg);
1612 mutt_param_set(&b_sign->parameter, "micalg", micalg);
1613 FREE(&micalg);
1614
1615 mutt_param_set(&b_sign->parameter, "protocol", "application/pkcs7-signature");
1616
1617 b_sign->parts = b;
1618 rc = b_sign;
1619
1620 b_sign->parts->next = mutt_body_new();
1621 b_sign = b_sign->parts->next;
1622 b_sign->type = TYPE_APPLICATION;
1623 b_sign->subtype = mutt_str_dup("pkcs7-signature");
1624 b_sign->filename = buf_strdup(signedfile);
1625 b_sign->d_filename = mutt_str_dup("smime.p7s");
1626 b_sign->use_disp = true;
1627 b_sign->disposition = DISP_ATTACH;
1628 b_sign->encoding = ENC_BASE64;
1629 b_sign->unlink = true; /* ok to remove this file after sending. */
1630
1631cleanup:
1632 if (fp_sign)
1633 {
1634 mutt_file_fclose(&fp_sign);
1635 mutt_file_unlink(buf_string(filetosign));
1636 }
1637 if (fp_smime_out)
1638 {
1639 mutt_file_fclose(&fp_smime_out);
1640 mutt_file_unlink(buf_string(signedfile));
1641 }
1642 buf_pool_release(&filetosign);
1643 buf_pool_release(&signedfile);
1644 return rc;
1645}
1646
1664static pid_t smime_invoke_verify(FILE **fp_smime_in, FILE **fp_smime_out,
1665 FILE **fp_smime_err, int fp_smime_infd,
1666 int fp_smime_outfd, int fp_smime_errfd,
1667 const char *fname, const char *sig_fname, int opaque)
1668{
1669 const struct Expando *c_smime_verify_opaque_command =
1670 cs_subset_expando(NeoMutt->sub, "smime_verify_opaque_command");
1671 const struct Expando *c_smime_verify_command = cs_subset_expando(NeoMutt->sub, "smime_verify_command");
1672 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd, fp_smime_outfd,
1673 fp_smime_errfd, fname, sig_fname, NULL, NULL, NULL, NULL, NULL,
1674 (opaque ? c_smime_verify_opaque_command : c_smime_verify_command));
1675}
1676
1692static pid_t smime_invoke_decrypt(FILE **fp_smime_in, FILE **fp_smime_out,
1693 FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd,
1694 int fp_smime_errfd, const char *fname)
1695{
1696 const struct Expando *c_smime_decrypt_command = cs_subset_expando(NeoMutt->sub, "smime_decrypt_command");
1697 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd,
1698 fp_smime_outfd, fp_smime_errfd, fname, NULL, NULL, NULL,
1700 NULL, c_smime_decrypt_command);
1701}
1702
1706int smime_class_verify_one(struct Body *b, struct State *state, const char *tempfile)
1707{
1708 FILE *fp = NULL, *fp_smime_out = NULL, *fp_smime_err = NULL;
1709 pid_t pid;
1710 int badsig = -1;
1711
1712 LOFF_T tmpoffset = 0;
1713 size_t tmplength = 0;
1714 int orig_type = b->type;
1715
1716 struct Buffer *signedfile = buf_pool_get();
1717
1718 buf_printf(signedfile, "%s.sig", tempfile);
1719
1720 /* decode to a tempfile, saving the original destination */
1721 fp = state->fp_out;
1722 state->fp_out = mutt_file_fopen(buf_string(signedfile), "w");
1723 if (!state->fp_out)
1724 {
1725 mutt_perror("%s", buf_string(signedfile));
1726 goto cleanup;
1727 }
1728 /* decoding the attachment changes the size and offset, so save a copy
1729 * of the "real" values now, and restore them after processing */
1730 tmplength = b->length;
1731 tmpoffset = b->offset;
1732
1733 /* if we are decoding binary bodies, we don't want to prefix each
1734 * line with the prefix or else the data will get corrupted. */
1735 const char *save_prefix = state->prefix;
1736 state->prefix = NULL;
1737
1738 mutt_decode_attachment(b, state);
1739
1740 b->length = ftello(state->fp_out);
1741 b->offset = 0;
1742 mutt_file_fclose(&state->fp_out);
1743
1744 /* restore final destination and substitute the tempfile for input */
1745 state->fp_out = fp;
1746 fp = state->fp_in;
1747 state->fp_in = mutt_file_fopen(buf_string(signedfile), "r");
1748
1749 /* restore the prefix */
1750 state->prefix = save_prefix;
1751
1752 b->type = orig_type;
1753
1754 fp_smime_err = mutt_file_mkstemp();
1755 if (!fp_smime_err)
1756 {
1757 mutt_perror(_("Can't create temporary file"));
1758 goto cleanup;
1759 }
1760
1761 crypt_current_time(state, "OpenSSL");
1762
1763 pid = smime_invoke_verify(NULL, &fp_smime_out, NULL, -1, -1, fileno(fp_smime_err),
1764 tempfile, buf_string(signedfile), 0);
1765 if (pid != -1)
1766 {
1767 fflush(fp_smime_out);
1768 mutt_file_fclose(&fp_smime_out);
1769
1770 if (filter_wait(pid))
1771 {
1772 badsig = -1;
1773 }
1774 else
1775 {
1776 char *line = NULL;
1777 size_t linelen;
1778
1779 fflush(fp_smime_err);
1780 rewind(fp_smime_err);
1781
1782 line = mutt_file_read_line(line, &linelen, fp_smime_err, NULL, MUTT_RL_NO_FLAGS);
1783 if (linelen && mutt_istr_equal(line, "verification successful"))
1784 badsig = 0;
1785
1786 FREE(&line);
1787 }
1788 }
1789
1790 fflush(fp_smime_err);
1791 rewind(fp_smime_err);
1792 mutt_file_copy_stream(fp_smime_err, state->fp_out);
1793 mutt_file_fclose(&fp_smime_err);
1794
1795 state_attach_puts(state, _("[-- End of OpenSSL output --]\n\n"));
1796
1797 mutt_file_unlink(buf_string(signedfile));
1798
1799 b->length = tmplength;
1800 b->offset = tmpoffset;
1801
1802 /* restore the original source stream */
1803 mutt_file_fclose(&state->fp_in);
1804 state->fp_in = fp;
1805
1806cleanup:
1807 buf_pool_release(&signedfile);
1808 return badsig;
1809}
1810
1820static struct Body *smime_handle_entity(struct Body *b, struct State *state, FILE *fp_out_file)
1821{
1822 struct Buffer *tmpfname = buf_pool_get();
1823 FILE *fp_smime_out = NULL, *fp_smime_in = NULL, *fp_smime_err = NULL;
1824 FILE *fp_tmp = NULL, *fp_out = NULL;
1825 struct Body *p = NULL;
1826 pid_t pid = -1;
1828
1829 if (!(type & APPLICATION_SMIME))
1830 return NULL;
1831
1832 /* Because of the mutt_body_handler() we avoid the buffer pool. */
1833 fp_smime_out = mutt_file_mkstemp();
1834 if (!fp_smime_out)
1835 {
1836 mutt_perror(_("Can't create temporary file"));
1837 goto cleanup;
1838 }
1839
1840 fp_smime_err = mutt_file_mkstemp();
1841 if (!fp_smime_err)
1842 {
1843 mutt_perror(_("Can't create temporary file"));
1844 goto cleanup;
1845 }
1846
1847 buf_mktemp(tmpfname);
1848 fp_tmp = mutt_file_fopen(buf_string(tmpfname), "w+");
1849 if (!fp_tmp)
1850 {
1851 mutt_perror("%s", buf_string(tmpfname));
1852 goto cleanup;
1853 }
1854
1855 if (!mutt_file_seek(state->fp_in, b->offset, SEEK_SET))
1856 {
1857 goto cleanup;
1858 }
1859
1860 mutt_file_copy_bytes(state->fp_in, fp_tmp, b->length);
1861
1862 fflush(fp_tmp);
1863 mutt_file_fclose(&fp_tmp);
1864
1865 if ((type & SEC_ENCRYPT) &&
1866 ((pid = smime_invoke_decrypt(&fp_smime_in, NULL, NULL, -1, fileno(fp_smime_out),
1867 fileno(fp_smime_err), buf_string(tmpfname))) == -1))
1868 {
1869 mutt_file_unlink(buf_string(tmpfname));
1870 if (state->flags & STATE_DISPLAY)
1871 {
1872 state_attach_puts(state, _("[-- Error: unable to create OpenSSL subprocess --]\n"));
1873 }
1874 goto cleanup;
1875 }
1876 else if ((type & SEC_SIGNOPAQUE) &&
1877 ((pid = smime_invoke_verify(&fp_smime_in, NULL, NULL, -1,
1878 fileno(fp_smime_out), fileno(fp_smime_err), NULL,
1879 buf_string(tmpfname), SEC_SIGNOPAQUE)) == -1))
1880 {
1881 mutt_file_unlink(buf_string(tmpfname));
1882 if (state->flags & STATE_DISPLAY)
1883 {
1884 state_attach_puts(state, _("[-- Error: unable to create OpenSSL subprocess --]\n"));
1885 }
1886 goto cleanup;
1887 }
1888
1889 if (type & SEC_ENCRYPT)
1890 {
1893 fputs(SmimePass, fp_smime_in);
1894 fputc('\n', fp_smime_in);
1895 }
1896
1897 mutt_file_fclose(&fp_smime_in);
1898
1899 filter_wait(pid);
1900 mutt_file_unlink(buf_string(tmpfname));
1901
1902 if (state->flags & STATE_DISPLAY)
1903 {
1904 fflush(fp_smime_err);
1905 rewind(fp_smime_err);
1906
1907 const int c = fgetc(fp_smime_err);
1908 if (c != EOF)
1909 {
1910 ungetc(c, fp_smime_err);
1911
1912 crypt_current_time(state, "OpenSSL");
1913 mutt_file_copy_stream(fp_smime_err, state->fp_out);
1914 state_attach_puts(state, _("[-- End of OpenSSL output --]\n\n"));
1915 }
1916
1917 if (type & SEC_ENCRYPT)
1918 {
1919 state_attach_puts(state, _("[-- The following data is S/MIME encrypted --]\n"));
1920 }
1921 else
1922 {
1923 state_attach_puts(state, _("[-- The following data is S/MIME signed --]\n"));
1924 }
1925 }
1926
1927 fflush(fp_smime_out);
1928 rewind(fp_smime_out);
1929
1930 if (type & SEC_ENCRYPT)
1931 {
1932 /* void the passphrase, even if that wasn't the problem */
1933 if (fgetc(fp_smime_out) == EOF)
1934 {
1935 mutt_error(_("Decryption failed"));
1937 }
1938 rewind(fp_smime_out);
1939 }
1940
1941 if (fp_out_file)
1942 {
1943 fp_out = fp_out_file;
1944 }
1945 else
1946 {
1947 fp_out = mutt_file_mkstemp();
1948 if (!fp_out)
1949 {
1950 mutt_perror(_("Can't create temporary file"));
1951 goto cleanup;
1952 }
1953 }
1954 char buf[8192] = { 0 };
1955 while (fgets(buf, sizeof(buf) - 1, fp_smime_out))
1956 {
1957 const size_t len = mutt_str_len(buf);
1958 if ((len > 1) && (buf[len - 2] == '\r'))
1959 {
1960 buf[len - 2] = '\n';
1961 buf[len - 1] = '\0';
1962 }
1963 fputs(buf, fp_out);
1964 }
1965 fflush(fp_out);
1966 rewind(fp_out);
1967
1968 const long size = mutt_file_get_size_fp(fp_out);
1969 if (size == 0)
1970 {
1971 goto cleanup;
1972 }
1973 p = mutt_read_mime_header(fp_out, 0);
1974 if (p)
1975 {
1976 p->length = size - p->offset;
1977
1978 mutt_parse_part(fp_out, p);
1979
1980 if (state->flags & STATE_DISPLAY)
1982
1983 /* Store any protected headers in the parent so they can be
1984 * accessed for index updates after the handler recursion is done.
1985 * This is done before the handler to prevent a nested encrypted
1986 * handler from freeing the headers. */
1988 b->mime_headers = p->mime_headers;
1989 p->mime_headers = NULL;
1990
1991 if (state->fp_out)
1992 {
1993 rewind(fp_out);
1994 FILE *fp_tmp_buffer = state->fp_in;
1995 state->fp_in = fp_out;
1996 mutt_body_handler(p, state);
1997 state->fp_in = fp_tmp_buffer;
1998 }
1999
2000 /* Embedded multipart signed protected headers override the
2001 * encrypted headers. We need to do this after the handler so
2002 * they can be printed in the pager. */
2003 if (!(type & SMIME_SIGN) && mutt_is_multipart_signed(p) && p->parts &&
2004 p->parts->mime_headers)
2005 {
2008 p->parts->mime_headers = NULL;
2009 }
2010 }
2011 mutt_file_fclose(&fp_smime_out);
2012
2013 if (!fp_out_file)
2014 {
2015 mutt_file_fclose(&fp_out);
2016 mutt_file_unlink(buf_string(tmpfname));
2017 }
2018 fp_out = NULL;
2019
2020 if (state->flags & STATE_DISPLAY)
2021 {
2022 if (type & SEC_ENCRYPT)
2023 state_attach_puts(state, _("[-- End of S/MIME encrypted data --]\n"));
2024 else
2025 state_attach_puts(state, _("[-- End of S/MIME signed data --]\n"));
2026 }
2027
2028 if (type & SEC_SIGNOPAQUE)
2029 {
2030 char *line = NULL;
2031 size_t linelen;
2032
2033 rewind(fp_smime_err);
2034
2035 line = mutt_file_read_line(line, &linelen, fp_smime_err, NULL, MUTT_RL_NO_FLAGS);
2036 if (linelen && mutt_istr_equal(line, "verification successful"))
2037 b->goodsig = true;
2038 FREE(&line);
2039 }
2040 else if (p)
2041 {
2042 b->goodsig = p->goodsig;
2043 b->badsig = p->badsig;
2044 }
2045
2046cleanup:
2047 mutt_file_fclose(&fp_smime_out);
2048 mutt_file_fclose(&fp_smime_err);
2049 mutt_file_fclose(&fp_tmp);
2050 mutt_file_fclose(&fp_out);
2051 buf_pool_release(&tmpfname);
2052 return p;
2053}
2054
2058int smime_class_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **b_dec)
2059{
2060 struct State state = { 0 };
2061 LOFF_T tmpoffset = b->offset;
2062 size_t tmplength = b->length;
2063 int rc = -1;
2064
2066 return -1;
2067
2068 if (b->parts)
2069 return -1;
2070
2071 state.fp_in = fp_in;
2072 if (!mutt_file_seek(state.fp_in, b->offset, SEEK_SET))
2073 {
2074 return -1;
2075 }
2076
2077 FILE *fp_tmp = mutt_file_mkstemp();
2078 if (!fp_tmp)
2079 {
2080 mutt_perror(_("Can't create temporary file"));
2081 return -1;
2082 }
2083
2084 state.fp_out = fp_tmp;
2085 mutt_decode_attachment(b, &state);
2086 fflush(fp_tmp);
2087 b->length = ftello(state.fp_out);
2088 b->offset = 0;
2089 rewind(fp_tmp);
2090 state.fp_in = fp_tmp;
2091 state.fp_out = 0;
2092
2094 if (!*fp_out)
2095 {
2096 mutt_perror(_("Can't create temporary file"));
2097 goto bail;
2098 }
2099
2100 *b_dec = smime_handle_entity(b, &state, *fp_out);
2101 if (!*b_dec)
2102 goto bail;
2103
2104 (*b_dec)->goodsig = b->goodsig;
2105 (*b_dec)->badsig = b->badsig;
2106 rc = 0;
2107
2108bail:
2109 b->length = tmplength;
2110 b->offset = tmpoffset;
2111 mutt_file_fclose(&fp_tmp);
2112 if (*fp_out)
2113 rewind(*fp_out);
2114
2115 return rc;
2116}
2117
2121int smime_class_application_handler(struct Body *b, struct State *state)
2122{
2123 int rc = -1;
2124
2125 /* clear out any mime headers before the handler, so they can't be spoofed. */
2127
2128 struct Body *tattach = smime_handle_entity(b, state, NULL);
2129 if (tattach)
2130 {
2131 rc = 0;
2132 mutt_body_free(&tattach);
2133 }
2134 return rc;
2135}
2136
2141{
2142 struct SmimeKey *key = NULL;
2143 const char *prompt = NULL;
2144 const char *letters = NULL;
2145 const char *choices = NULL;
2146 int choice;
2147
2149 return e->security;
2150
2152
2153 /* Opportunistic encrypt is controlling encryption.
2154 * NOTE: "Signing" and "Clearing" only adjust the sign bit, so we have different
2155 * letter choices for those. */
2156 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt");
2157 if (c_crypt_opportunistic_encrypt && (e->security & SEC_OPPENCRYPT))
2158 {
2159 /* L10N: S/MIME options (opportunistic encryption is on) */
2160 prompt = _("S/MIME (s)ign, encrypt (w)ith, sign (a)s, (c)lear, or (o)ppenc mode off?");
2161 /* L10N: S/MIME options (opportunistic encryption is on) */
2162 letters = _("swaco");
2163 choices = "SwaCo";
2164 }
2165 else if (c_crypt_opportunistic_encrypt)
2166 {
2167 /* Opportunistic encryption option is set, but is toggled off
2168 * for this message. */
2169 /* L10N: S/MIME options (opportunistic encryption is off) */
2170 prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, (c)lear, or (o)ppenc mode?");
2171 /* L10N: S/MIME options (opportunistic encryption is off) */
2172 letters = _("eswabco");
2173 choices = "eswabcO";
2174 }
2175 else
2176 {
2177 /* Opportunistic encryption is unset */
2178 /* L10N: S/MIME options */
2179 prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear?");
2180 /* L10N: S/MIME options */
2181 letters = _("eswabc");
2182 choices = "eswabc";
2183 }
2184
2185 choice = mw_multi_choice(prompt, letters);
2186 if (choice > 0)
2187 {
2188 switch (choices[choice - 1])
2189 {
2190 case 'a': /* sign (a)s */
2191 key = smime_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN, false);
2192 if (key)
2193 {
2194 cs_subset_str_string_set(NeoMutt->sub, "smime_sign_as", key->hash, NULL);
2195 smime_key_free(&key);
2196
2197 e->security |= SEC_SIGN;
2198
2199 /* probably need a different passphrase */
2201 }
2202
2203 break;
2204
2205 case 'b': /* (b)oth */
2206 e->security |= (SEC_ENCRYPT | SEC_SIGN);
2207 break;
2208
2209 case 'c': /* (c)lear */
2210 e->security &= ~(SEC_ENCRYPT | SEC_SIGN);
2211 break;
2212
2213 case 'C':
2214 e->security &= ~SEC_SIGN;
2215 break;
2216
2217 case 'e': /* (e)ncrypt */
2218 e->security |= SEC_ENCRYPT;
2219 e->security &= ~SEC_SIGN;
2220 break;
2221
2222 case 'O': /* oppenc mode on */
2225 break;
2226
2227 case 'o': /* oppenc mode off */
2228 e->security &= ~SEC_OPPENCRYPT;
2229 break;
2230
2231 case 'S': /* (s)ign in oppenc mode */
2232 e->security |= SEC_SIGN;
2233 break;
2234
2235 case 's': /* (s)ign */
2236 e->security &= ~SEC_ENCRYPT;
2237 e->security |= SEC_SIGN;
2238 break;
2239
2240 case 'w': /* encrypt (w)ith */
2241 {
2242 e->security |= SEC_ENCRYPT;
2243 do
2244 {
2245 struct Buffer *errmsg = buf_pool_get();
2246 int rc = CSR_SUCCESS;
2247 switch (mw_multi_choice(_("Choose algorithm family: (1) DES, (2) RC2, (3) AES, or (c)lear?"),
2248 // L10N: Options for: Choose algorithm family: (1) DES, (2) RC2, (3) AES, or (c)lear?
2249 _("123c")))
2250 {
2251 case 1:
2252 switch (choice = mw_multi_choice(_("(1) DES, (2) Triple-DES?"),
2253 // L10N: Options for: (1) DES, (2) Triple-DES
2254 _("12")))
2255 {
2256 case 1:
2257 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2258 "des", errmsg);
2259 break;
2260 case 2:
2261 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2262 "des3", errmsg);
2263 break;
2264 }
2265 break;
2266
2267 case 2:
2268 switch (choice = mw_multi_choice(_("(1) RC2-40, (2) RC2-64, (3) RC2-128?"),
2269 // L10N: Options for: (1) RC2-40, (2) RC2-64, (3) RC2-128
2270 _("123")))
2271 {
2272 case 1:
2273 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2274 "rc2-40", errmsg);
2275 break;
2276 case 2:
2277 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2278 "rc2-64", errmsg);
2279 break;
2280 case 3:
2281 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2282 "rc2-128", errmsg);
2283 break;
2284 }
2285 break;
2286
2287 case 3:
2288 switch (choice = mw_multi_choice(_("(1) AES128, (2) AES192, (3) AES256?"),
2289 // L10N: Options for: (1) AES128, (2) AES192, (3) AES256
2290 _("123")))
2291 {
2292 case 1:
2293 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2294 "aes128", errmsg);
2295 break;
2296 case 2:
2297 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2298 "aes192", errmsg);
2299 break;
2300 case 3:
2301 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2302 "aes256", errmsg);
2303 break;
2304 }
2305 break;
2306
2307 case 4:
2308 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with", NULL, errmsg);
2309 /* (c)lear */
2311
2312 case -1: /* Ctrl-G or Enter */
2313 choice = 0;
2314 break;
2315 }
2316
2317 if ((CSR_RESULT(rc) != CSR_SUCCESS) && !buf_is_empty(errmsg))
2318 mutt_error("%s", buf_string(errmsg));
2319
2320 buf_pool_release(&errmsg);
2321 } while (choice == -1);
2322 break;
2323 }
2324 }
2325 }
2326
2327 return e->security;
2328}
2329
2336 // clang-format off
2345 { -1, -1, NULL, NULL },
2346 // clang-format on
2347};
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:56
@ 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:1817
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1357
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:109
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:149
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:148
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:40
char ** EnvList
Private copy of the environment variables.
Definition: globals.c:78
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:2121
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:2058
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:798
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:2140
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:1497
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:1332
void smime_class_getkeys(struct Envelope *env)
Get the S/MIME keys required to encrypt this email - Implements CryptModuleSpecs::smime_getkeys() -.
Definition: smime.c:758
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:1130
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:1206
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:1706
void smime_class_void_passphrase(void)
Forget the cached passphrase - Implements CryptModuleSpecs::void_passphrase() -.
Definition: smime.c:156
void smime_command_i(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Smime Command: Intermediate certificates - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:294
void smime_command_k(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Smime Command: Key-pair - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:308
void smime_command_C(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Smime Command: CA location - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:231
void smime_command_a(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Smime Command: algorithm - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:203
void smime_command_d(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Smime Command: Message digest algorithm - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:266
void smime_command_s(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Smime Command: Filename of signature - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:322
void smime_command_f(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Smime Command: Filename of message - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:280
void smime_command_c(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, 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:200
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:56
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:51
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:115
#define FREE(x)
Definition: memory.h:45
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:1317
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:1664
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:1062
static struct Body * smime_handle_entity(struct Body *b, struct State *state, FILE *fp_out_file)
Handle type application/pkcs7-mime.
Definition: smime.c:1820
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:1692
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:947
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:845
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 struct SmimeKey * smime_ask_for_key(char *prompt, KeyFlags abilities, bool only_public_key)
Ask the user to select a key.
Definition: smime.c:686
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:1290
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:1476
static void smime_command(struct Buffer *buf, struct SmimeCommandContext *cctx, const struct Expando *exp)
Format an SMIME command string.
Definition: smime.c:339
SMIME helper routines.
@ ED_SMI_MESSAGE_FILE
SmimeCommandContext.fname.
Definition: smime.h:79
@ ED_SMI_ALGORITHM
SmimeCommandContext.cryptalg.
Definition: smime.h:74
@ ED_SMI_SIGNATURE_FILE
SmimeCommandContext.sig_fname.
Definition: smime.h:80
@ ED_SMI_DIGEST_ALGORITHM
SmimeCommandContext.digestalg.
Definition: smime.h:76
@ ED_SMI_CERTIFICATE_IDS
SmimeCommandContext.certificates.
Definition: smime.h:75
@ ED_SMI_KEY
SmimeCommandContext.key.
Definition: smime.h:78
@ ED_SMI_INTERMEDIATE_IDS
SmimeCommandContext.intermediates.
Definition: smime.h:77
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:72
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:67
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:75
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:62
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:71
char * subtype
content-type subtype
Definition: body.h:60
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:58
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:69
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:57
const char * sig_fname
s
Definition: smime.h:62
const char * intermediates
i
Definition: smime.h:64
const char * digestalg
d
Definition: smime.h:60
const char * cryptalg
a
Definition: smime.h:59
const char * key
k
Definition: smime.h:58
const char * fname
f
Definition: smime.h:61
const char * certificates
c
Definition: smime.h:63
An SIME key.
Definition: smime.h:43
KeyFlags flags
Definition: smime.h:49
char * hash
Definition: smime.h:45
struct SmimeKey * next
Definition: smime.h:50
char * issuer
Definition: smime.h:47
char * email
Definition: smime.h:44
char * label
Definition: smime.h:46
char trust
i=Invalid r=revoked e=expired u=unverified v=verified t=trusted
Definition: smime.h:48
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