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