NeoMutt  2023-05-17-56-ga67199
Teaching an old dog new tricks
DOXYGEN
smime.c
Go to the documentation of this file.
1
32#include "config.h"
33#include <limits.h>
34#include <stdbool.h>
35#include <stdint.h>
36#include <stdio.h>
37#include <string.h>
38#include <sys/stat.h>
39#include <time.h>
40#include "private.h"
41#include "mutt/lib.h"
42#include "address/lib.h"
43#include "config/lib.h"
44#include "email/lib.h"
45#include "core/lib.h"
46#include "alias/lib.h"
47#include "gui/lib.h"
48#include "mutt.h"
49#include "lib.h"
50#include "enter/lib.h"
51#include "question/lib.h"
52#include "send/lib.h"
53#include "copy.h"
54#include "crypt.h"
55#include "cryptglue.h"
56#include "format_flags.h"
57#include "handler.h"
58#include "mutt_logging.h"
59#include "muttlib.h"
60#ifdef CRYPT_BACKEND_CLASSIC_SMIME
61#include "smime.h"
62#endif
63
68{
69 const char *key;
70 const char *cryptalg;
71 const char *digestalg;
72 const char *fname;
73 const char *sig_fname;
74 const char *certificates;
75 const char *intermediates;
76};
77
79static char SmimePass[256];
81static time_t SmimeExpTime = 0; /* when does the cached passphrase expire? */
82
84static struct Buffer SmimeKeyToUse = { 0 };
86static struct Buffer SmimeCertToUse = { 0 };
88static struct Buffer SmimeIntermediateToUse = { 0 };
89
93void smime_init(void)
94{
98}
99
104{
108}
109
114static void smime_key_free(struct SmimeKey **keylist)
115{
116 if (!keylist)
117 return;
118
119 struct SmimeKey *key = NULL;
120
121 while (*keylist)
122 {
123 key = *keylist;
124 *keylist = (*keylist)->next;
125
126 FREE(&key->email);
127 FREE(&key->hash);
128 FREE(&key->label);
129 FREE(&key->issuer);
130 FREE(&key);
131 }
132}
133
139static struct SmimeKey *smime_copy_key(struct SmimeKey *key)
140{
141 if (!key)
142 return NULL;
143
144 struct SmimeKey *copy = NULL;
145
146 copy = mutt_mem_calloc(1, sizeof(struct SmimeKey));
147 copy->email = mutt_str_dup(key->email);
148 copy->hash = mutt_str_dup(key->hash);
149 copy->label = mutt_str_dup(key->label);
150 copy->issuer = mutt_str_dup(key->issuer);
151 copy->trust = key->trust;
152 copy->flags = key->flags;
153
154 return copy;
155}
156
157/*
158 * Queries and passphrase handling.
159 */
160
165{
166 memset(SmimePass, 0, sizeof(SmimePass));
167 SmimeExpTime = 0;
168}
169
174{
175 const time_t now = mutt_date_now();
176 if (now < SmimeExpTime)
177 {
178 /* Use cached copy. */
179 return true;
180 }
181
183
184 struct Buffer *buf = buf_pool_get();
185 const int rc = buf_get_field(_("Enter S/MIME passphrase:"), buf,
187 NULL, NULL, NULL);
189 buf_pool_release(&buf);
190
191 if (rc == 0)
192 {
193 const short c_smime_timeout = cs_subset_number(NeoMutt->sub, "smime_timeout");
194 SmimeExpTime = mutt_date_add_timeout(now, c_smime_timeout);
195 return true;
196 }
197 else
198 {
199 SmimeExpTime = 0;
200 }
201
202 return false;
203}
204
205/*
206 * The OpenSSL interface
207 */
208
223static const char *smime_command_format_str(char *buf, size_t buflen, size_t col,
224 int cols, char op, const char *src,
225 const char *prec, const char *if_str,
226 const char *else_str, intptr_t data,
227 MuttFormatFlags flags)
228{
229 char fmt[128] = { 0 };
230 struct SmimeCommandContext *cctx = (struct SmimeCommandContext *) data;
231 bool optional = (flags & MUTT_FORMAT_OPTIONAL);
232
233 switch (op)
234 {
235 case 'C':
236 {
237 const char *const c_smime_ca_location = cs_subset_path(NeoMutt->sub, "smime_ca_location");
238 if (!optional)
239 {
240 struct Buffer *path = buf_pool_get();
241 struct Buffer *buf1 = buf_pool_get();
242 struct Buffer *buf2 = buf_pool_get();
243 struct stat st = { 0 };
244
245 buf_strcpy(path, c_smime_ca_location);
246 buf_expand_path(path);
247 buf_quote_filename(buf1, buf_string(path), true);
248
249 if ((stat(buf_string(path), &st) != 0) || !S_ISDIR(st.st_mode))
250 buf_printf(buf2, "-CAfile %s", buf_string(buf1));
251 else
252 buf_printf(buf2, "-CApath %s", buf_string(buf1));
253
254 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
255 snprintf(buf, buflen, fmt, buf_string(buf2));
256
257 buf_pool_release(&path);
258 buf_pool_release(&buf1);
259 buf_pool_release(&buf2);
260 }
261 else if (!c_smime_ca_location)
262 {
263 optional = false;
264 }
265 break;
266 }
267
268 case 'c':
269 { /* certificate (list) */
270 if (!optional)
271 {
272 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
273 snprintf(buf, buflen, fmt, NONULL(cctx->certificates));
274 }
275 else if (!cctx->certificates)
276 {
277 optional = false;
278 }
279 break;
280 }
281
282 case 'i':
283 { /* intermediate certificates */
284 if (!optional)
285 {
286 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
287 snprintf(buf, buflen, fmt, NONULL(cctx->intermediates));
288 }
289 else if (!cctx->intermediates)
290 {
291 optional = false;
292 }
293 break;
294 }
295
296 case 's':
297 { /* detached signature */
298 if (!optional)
299 {
300 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
301 snprintf(buf, buflen, fmt, NONULL(cctx->sig_fname));
302 }
303 else if (!cctx->sig_fname)
304 {
305 optional = false;
306 }
307 break;
308 }
309
310 case 'k':
311 { /* private key */
312 if (!optional)
313 {
314 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
315 snprintf(buf, buflen, fmt, NONULL(cctx->key));
316 }
317 else if (!cctx->key)
318 {
319 optional = false;
320 }
321 break;
322 }
323
324 case 'a':
325 { /* algorithm for encryption */
326 if (!optional)
327 {
328 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
329 snprintf(buf, buflen, fmt, NONULL(cctx->cryptalg));
330 }
331 else if (!cctx->key)
332 {
333 optional = false;
334 }
335 break;
336 }
337
338 case 'f':
339 { /* file to process */
340 if (!optional)
341 {
342 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
343 snprintf(buf, buflen, fmt, NONULL(cctx->fname));
344 }
345 else if (!cctx->fname)
346 {
347 optional = false;
348 }
349 break;
350 }
351
352 case 'd':
353 { /* algorithm for the signature message digest */
354 if (!optional)
355 {
356 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
357 snprintf(buf, buflen, fmt, NONULL(cctx->digestalg));
358 }
359 else if (!cctx->key)
360 {
361 optional = false;
362 }
363 break;
364 }
365
366 default:
367 *buf = '\0';
368 break;
369 }
370
371 if (optional)
372 {
373 mutt_expando_format(buf, buflen, col, cols, if_str,
375 }
376 else if (flags & MUTT_FORMAT_OPTIONAL)
377 {
378 mutt_expando_format(buf, buflen, col, cols, else_str,
380 }
381
382 /* We return the format string, unchanged */
383 return src;
384}
385
395static void smime_command(char *buf, size_t buflen,
396 struct SmimeCommandContext *cctx, const char *fmt)
397{
398 mutt_expando_format(buf, buflen, 0, buflen, NONULL(fmt), smime_command_format_str,
399 (intptr_t) cctx, MUTT_FORMAT_NO_FLAGS);
400 mutt_debug(LL_DEBUG2, "%s\n", buf);
401}
402
425static pid_t smime_invoke(FILE **fp_smime_in, FILE **fp_smime_out, FILE **fp_smime_err,
426 int fp_smime_infd, int fp_smime_outfd, int fp_smime_errfd,
427 const char *fname, const char *sig_fname, const char *cryptalg,
428 const char *digestalg, const char *key, const char *certificates,
429 const char *intermediates, const char *format)
430{
431 struct SmimeCommandContext cctx = { 0 };
432 char cmd[STR_COMMAND] = { 0 };
433
434 if (!format || (*format == '\0'))
435 return (pid_t) -1;
436
437 cctx.fname = fname;
438 cctx.sig_fname = sig_fname;
439 cctx.key = key;
440 cctx.cryptalg = cryptalg;
441 cctx.digestalg = digestalg;
444
445 smime_command(cmd, sizeof(cmd), &cctx, format);
446
447 return filter_create_fd(cmd, fp_smime_in, fp_smime_out, fp_smime_err,
448 fp_smime_infd, fp_smime_outfd, fp_smime_errfd);
449}
450
457static struct SmimeKey *smime_parse_key(char *buf)
458{
459 char *pend = NULL, *p = NULL;
460 int field = 0;
461
462 struct SmimeKey *key = mutt_mem_calloc(1, sizeof(struct SmimeKey));
463
464 for (p = buf; p; p = pend)
465 {
466 /* Some users manually maintain their .index file, and use a tab
467 * as a delimiter, which the old parsing code (using fscanf)
468 * happened to allow. smime_keys uses a space, so search for both. */
469 if ((pend = strchr(p, ' ')) || (pend = strchr(p, '\t')) || (pend = strchr(p, '\n')))
470 *pend++ = 0;
471
472 /* For backward compatibility, don't count consecutive delimiters
473 * as an empty field. */
474 if (*p == '\0')
475 continue;
476
477 field++;
478
479 switch (field)
480 {
481 case 1: /* mailbox */
482 key->email = mutt_str_dup(p);
483 break;
484 case 2: /* hash */
485 key->hash = mutt_str_dup(p);
486 break;
487 case 3: /* label */
488 key->label = mutt_str_dup(p);
489 break;
490 case 4: /* issuer */
491 key->issuer = mutt_str_dup(p);
492 break;
493 case 5: /* trust */
494 key->trust = *p;
495 break;
496 case 6: /* purpose */
497 while (*p)
498 {
499 switch (*p++)
500 {
501 case 'e':
503 break;
504
505 case 's':
506 key->flags |= KEYFLAG_CANSIGN;
507 break;
508 }
509 }
510 break;
511 }
512 }
513
514 /* Old index files could be missing issuer, trust, and purpose,
515 * but anything less than that is an error. */
516 if (field < 3)
517 {
518 smime_key_free(&key);
519 return NULL;
520 }
521
522 if (field < 4)
523 key->issuer = mutt_str_dup("?");
524
525 if (field < 5)
526 key->trust = 't';
527
528 if (field < 6)
530
531 return key;
532}
533
540static struct SmimeKey *smime_get_candidates(const char *search, bool only_public_key)
541{
542 char buf[1024] = { 0 };
543 struct SmimeKey *key = NULL, *results = NULL;
544 struct SmimeKey **results_end = &results;
545
546 struct Buffer *index_file = buf_pool_get();
547 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
548 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
549 buf_printf(index_file, "%s/.index",
550 only_public_key ? NONULL(c_smime_certificates) : NONULL(c_smime_keys));
551
552 FILE *fp = mutt_file_fopen(buf_string(index_file), "r");
553 if (!fp)
554 {
555 mutt_perror(buf_string(index_file));
556 buf_pool_release(&index_file);
557 return NULL;
558 }
559 buf_pool_release(&index_file);
560
561 while (fgets(buf, sizeof(buf), fp))
562 {
563 if (((*search == '\0')) || mutt_istr_find(buf, search))
564 {
565 key = smime_parse_key(buf);
566 if (key)
567 {
568 *results_end = key;
569 results_end = &key->next;
570 }
571 }
572 }
573
574 mutt_file_fclose(&fp);
575
576 return results;
577}
578
588static struct SmimeKey *smime_get_key_by_hash(const char *hash, bool only_public_key)
589{
590 struct SmimeKey *match = NULL;
591 struct SmimeKey *results = smime_get_candidates(hash, only_public_key);
592 for (struct SmimeKey *result = results; result; result = result->next)
593 {
594 if (mutt_istr_equal(hash, result->hash))
595 {
596 match = smime_copy_key(result);
597 break;
598 }
599 }
600
601 smime_key_free(&results);
602
603 return match;
604}
605
614static struct SmimeKey *smime_get_key_by_addr(const char *mailbox, KeyFlags abilities,
615 bool only_public_key, bool oppenc_mode)
616{
617 if (!mailbox)
618 return NULL;
619
620 struct SmimeKey *results = NULL, *result = NULL;
621 struct SmimeKey *matches = NULL;
622 struct SmimeKey **matches_end = &matches;
623 struct SmimeKey *match = NULL;
624 struct SmimeKey *trusted_match = NULL;
625 struct SmimeKey *valid_match = NULL;
626 struct SmimeKey *return_key = NULL;
627 bool multi_trusted_matches = false;
628
629 results = smime_get_candidates(mailbox, only_public_key);
630 for (result = results; result; result = result->next)
631 {
632 if (abilities && !(result->flags & abilities))
633 {
634 continue;
635 }
636
637 if (mutt_istr_equal(mailbox, result->email))
638 {
639 match = smime_copy_key(result);
640 *matches_end = match;
641 matches_end = &match->next;
642
643 if (match->trust == 't')
644 {
645 if (trusted_match && !mutt_istr_equal(match->hash, trusted_match->hash))
646 {
647 multi_trusted_matches = true;
648 }
649 trusted_match = match;
650 }
651 else if ((match->trust == 'u') || (match->trust == 'v'))
652 {
653 valid_match = match;
654 }
655 }
656 }
657
658 smime_key_free(&results);
659
660 if (matches)
661 {
662 if (oppenc_mode)
663 {
664 const bool c_crypt_opportunistic_encrypt_strong_keys =
665 cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt_strong_keys");
666 if (trusted_match)
667 return_key = smime_copy_key(trusted_match);
668 else if (valid_match && !c_crypt_opportunistic_encrypt_strong_keys)
669 return_key = smime_copy_key(valid_match);
670 else
671 return_key = NULL;
672 }
673 else if (trusted_match && !multi_trusted_matches)
674 {
675 return_key = smime_copy_key(trusted_match);
676 }
677 else
678 {
679 return_key = smime_copy_key(dlg_select_smime_key(matches, mailbox));
680 }
681
682 smime_key_free(&matches);
683 }
684
685 return return_key;
686}
687
695static struct SmimeKey *smime_get_key_by_str(const char *str, KeyFlags abilities, bool only_public_key)
696{
697 if (!str)
698 return NULL;
699
700 struct SmimeKey *results = NULL, *result = NULL;
701 struct SmimeKey *matches = NULL;
702 struct SmimeKey **matches_end = &matches;
703 struct SmimeKey *match = NULL;
704 struct SmimeKey *return_key = NULL;
705
706 results = smime_get_candidates(str, only_public_key);
707 for (result = results; result; result = result->next)
708 {
709 if (abilities && !(result->flags & abilities))
710 {
711 continue;
712 }
713
714 if (mutt_istr_equal(str, result->hash) ||
715 mutt_istr_find(result->email, str) || mutt_istr_find(result->label, str))
716 {
717 match = smime_copy_key(result);
718 *matches_end = match;
719 matches_end = &match->next;
720 }
721 }
722
723 smime_key_free(&results);
724
725 if (matches)
726 {
727 return_key = smime_copy_key(dlg_select_smime_key(matches, str));
728 smime_key_free(&matches);
729 }
730
731 return return_key;
732}
733
741static struct SmimeKey *smime_ask_for_key(char *prompt, KeyFlags abilities, bool only_public_key)
742{
743 struct SmimeKey *key = NULL;
744 struct Buffer *resp = buf_pool_get();
745
746 if (!prompt)
747 prompt = _("Enter keyID: ");
748
750
751 while (true)
752 {
753 buf_reset(resp);
754 if (buf_get_field(prompt, resp, MUTT_COMP_NO_FLAGS, false, NULL, NULL, NULL) != 0)
755 {
756 goto done;
757 }
758
759 key = smime_get_key_by_str(buf_string(resp), abilities, only_public_key);
760 if (key)
761 goto done;
762
763 mutt_error(_("No matching keys found for \"%s\""), buf_string(resp));
764 }
765
766done:
767 buf_pool_release(&resp);
768 return key;
769}
770
778static void getkeys(const char *mailbox)
779{
780 const char *k = NULL;
781
782 struct SmimeKey *key = smime_get_key_by_addr(mailbox, KEYFLAG_CANENCRYPT, false, false);
783
784 if (!key)
785 {
786 char buf[256] = { 0 };
787 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), mailbox);
788 key = smime_ask_for_key(buf, KEYFLAG_CANENCRYPT, false);
789 }
790
791 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
792 size_t smime_keys_len = mutt_str_len(c_smime_keys);
793
794 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
795 k = key ? key->hash : NONULL(c_smime_default_key);
796
797 /* if the key is different from last time */
798 if ((buf_len(&SmimeKeyToUse) <= smime_keys_len) ||
799 !mutt_istr_equal(k, SmimeKeyToUse.data + smime_keys_len + 1))
800 {
802 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), k);
803 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
804 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), k);
805 }
806
807 smime_key_free(&key);
808}
809
814{
815 const bool c_smime_decrypt_use_default_key = cs_subset_bool(NeoMutt->sub, "smime_decrypt_use_default_key");
816 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
817 if (c_smime_decrypt_use_default_key && c_smime_default_key)
818 {
819 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
820 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), c_smime_default_key);
821 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
822 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), c_smime_default_key);
823 return;
824 }
825
826 struct Address *a = NULL;
827 TAILQ_FOREACH(a, &env->to, entries)
828 {
829 if (mutt_addr_is_user(a))
830 {
832 return;
833 }
834 }
835
836 TAILQ_FOREACH(a, &env->cc, entries)
837 {
838 if (mutt_addr_is_user(a))
839 {
841 return;
842 }
843 }
844
845 struct Address *f = mutt_default_from(NeoMutt->sub);
847 mutt_addr_free(&f);
848}
849
853char *smime_class_find_keys(const struct AddressList *al, bool oppenc_mode)
854{
855 struct SmimeKey *key = NULL;
856 char *keyid = NULL, *keylist = NULL;
857 size_t keylist_size = 0;
858 size_t keylist_used = 0;
859
860 struct Address *a = NULL;
861 TAILQ_FOREACH(a, al, entries)
862 {
863 key = smime_get_key_by_addr(buf_string(a->mailbox), KEYFLAG_CANENCRYPT, true, oppenc_mode);
864 if (!key && !oppenc_mode)
865 {
866 char buf[1024] = { 0 };
867 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), buf_string(a->mailbox));
868 key = smime_ask_for_key(buf, KEYFLAG_CANENCRYPT, true);
869 }
870 if (!key)
871 {
872 if (!oppenc_mode)
873 mutt_message(_("No (valid) certificate found for %s"), buf_string(a->mailbox));
874 FREE(&keylist);
875 return NULL;
876 }
877
878 keyid = key->hash;
879 keylist_size += mutt_str_len(keyid) + 2;
880 mutt_mem_realloc(&keylist, keylist_size);
881 sprintf(keylist + keylist_used, "%s%s", keylist_used ? " " : "", keyid);
882 keylist_used = mutt_str_len(keylist);
883
884 smime_key_free(&key);
885 }
886 return keylist;
887}
888
900static int smime_handle_cert_email(const char *certificate, const char *mailbox,
901 bool copy, char ***buffer, int *num)
902{
903 char email[256] = { 0 };
904 int rc = -1, count = 0;
905 pid_t pid;
906
907 FILE *fp_err = mutt_file_mkstemp();
908 if (!fp_err)
909 {
910 mutt_perror(_("Can't create temporary file"));
911 return 1;
912 }
913
914 FILE *fp_out = mutt_file_mkstemp();
915 if (!fp_out)
916 {
917 mutt_file_fclose(&fp_err);
918 mutt_perror(_("Can't create temporary file"));
919 return 1;
920 }
921
922 const char *const c_smime_get_cert_email_command = cs_subset_string(NeoMutt->sub, "smime_get_cert_email_command");
923 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_out), fileno(fp_err), certificate,
924 NULL, NULL, NULL, NULL, NULL, NULL, c_smime_get_cert_email_command);
925 if (pid == -1)
926 {
927 mutt_message(_("Error: unable to create OpenSSL subprocess"));
928 mutt_file_fclose(&fp_err);
929 mutt_file_fclose(&fp_out);
930 return 1;
931 }
932
933 filter_wait(pid);
934
935 fflush(fp_out);
936 rewind(fp_out);
937 fflush(fp_err);
938 rewind(fp_err);
939
940 while ((fgets(email, sizeof(email), fp_out)))
941 {
942 size_t len = mutt_str_len(email);
943 if (len && (email[len - 1] == '\n'))
944 email[len - 1] = '\0';
945 if (mutt_istr_startswith(email, mailbox))
946 rc = 1;
947
948 rc = (rc < 0) ? 0 : rc;
949 count++;
950 }
951
952 if (rc == -1)
953 {
954 mutt_endwin();
955 mutt_file_copy_stream(fp_err, stdout);
956 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
957 rc = 1;
958 }
959 else if (rc == 0)
960 {
961 rc = 1;
962 }
963 else
964 {
965 rc = 0;
966 }
967
968 if (copy && buffer && num)
969 {
970 (*num) = count;
971 *buffer = mutt_mem_calloc(count, sizeof(char *));
972 count = 0;
973
974 rewind(fp_out);
975 while ((fgets(email, sizeof(email), fp_out)))
976 {
977 size_t len = mutt_str_len(email);
978 if (len && (email[len - 1] == '\n'))
979 email[len - 1] = '\0';
980 (*buffer)[count] = mutt_mem_calloc(mutt_str_len(email) + 1, sizeof(char));
981 strncpy((*buffer)[count], email, mutt_str_len(email));
982 count++;
983 }
984 }
985 else if (copy)
986 {
987 rc = 2;
988 }
989
990 mutt_file_fclose(&fp_out);
991 mutt_file_fclose(&fp_err);
992
993 return rc;
994}
995
1001static char *smime_extract_certificate(const char *infile)
1002{
1003 FILE *fp_err = NULL;
1004 FILE *fp_out = NULL;
1005 FILE *fp_cert = NULL;
1006 char *retval = NULL;
1007 pid_t pid;
1008 int empty;
1009
1010 struct Buffer *pk7out = buf_pool_get();
1011 struct Buffer *certfile = buf_pool_get();
1012
1013 fp_err = mutt_file_mkstemp();
1014 if (!fp_err)
1015 {
1016 mutt_perror(_("Can't create temporary file"));
1017 goto cleanup;
1018 }
1019
1020 buf_mktemp(pk7out);
1021 fp_out = mutt_file_fopen(buf_string(pk7out), "w+");
1022 if (!fp_out)
1023 {
1024 mutt_perror(buf_string(pk7out));
1025 goto cleanup;
1026 }
1027
1028 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
1029 * extract the full set of certificates directly. */
1030 const char *const c_smime_pk7out_command = cs_subset_string(NeoMutt->sub, "smime_pk7out_command");
1031 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_out), fileno(fp_err), infile,
1032 NULL, NULL, NULL, NULL, NULL, NULL, c_smime_pk7out_command);
1033 if (pid == -1)
1034 {
1035 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
1036 goto cleanup;
1037 }
1038
1039 filter_wait(pid);
1040
1041 fflush(fp_out);
1042 rewind(fp_out);
1043 fflush(fp_err);
1044 rewind(fp_err);
1045 empty = (fgetc(fp_out) == EOF);
1046 if (empty)
1047 {
1048 mutt_perror(buf_string(pk7out));
1049 mutt_file_copy_stream(fp_err, stdout);
1050 goto cleanup;
1051 }
1052 mutt_file_fclose(&fp_out);
1053
1054 buf_mktemp(certfile);
1055 fp_cert = mutt_file_fopen(buf_string(certfile), "w+");
1056 if (!fp_cert)
1057 {
1058 mutt_perror(buf_string(certfile));
1060 goto cleanup;
1061 }
1062
1063 // Step 2: Extract the certificates from a PKCS#7 structure.
1064 const char *const c_smime_get_cert_command = cs_subset_string(NeoMutt->sub, "smime_get_cert_command");
1065 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_cert), fileno(fp_err),
1066 buf_string(pk7out), NULL, NULL, NULL, NULL, NULL, NULL,
1067 c_smime_get_cert_command);
1068 if (pid == -1)
1069 {
1070 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
1072 goto cleanup;
1073 }
1074
1075 filter_wait(pid);
1076
1078
1079 fflush(fp_cert);
1080 rewind(fp_cert);
1081 fflush(fp_err);
1082 rewind(fp_err);
1083 empty = (fgetc(fp_cert) == EOF);
1084 if (empty)
1085 {
1086 mutt_file_copy_stream(fp_err, stdout);
1087 goto cleanup;
1088 }
1089
1090 mutt_file_fclose(&fp_cert);
1091
1092 retval = buf_strdup(certfile);
1093
1094cleanup:
1095 mutt_file_fclose(&fp_err);
1096 if (fp_out)
1097 {
1098 mutt_file_fclose(&fp_out);
1100 }
1101 if (fp_cert)
1102 {
1103 mutt_file_fclose(&fp_cert);
1104 mutt_file_unlink(buf_string(certfile));
1105 }
1106 buf_pool_release(&pk7out);
1107 buf_pool_release(&certfile);
1108 return retval;
1109}
1110
1116static char *smime_extract_signer_certificate(const char *infile)
1117{
1118 char *cert = NULL;
1119 struct Buffer *certfile = NULL;
1120 pid_t pid;
1121 int empty;
1122
1123 FILE *fp_err = mutt_file_mkstemp();
1124 if (!fp_err)
1125 {
1126 mutt_perror(_("Can't create temporary file"));
1127 return NULL;
1128 }
1129
1130 certfile = buf_pool_get();
1131 buf_mktemp(certfile);
1132 FILE *fp_out = mutt_file_fopen(buf_string(certfile), "w+");
1133 if (!fp_out)
1134 {
1135 mutt_file_fclose(&fp_err);
1136 mutt_perror(buf_string(certfile));
1137 goto cleanup;
1138 }
1139
1140 /* Extract signer's certificate
1141 */
1142 const char *const c_smime_get_signer_cert_command = cs_subset_string(NeoMutt->sub, "smime_get_signer_cert_command");
1143 pid = smime_invoke(NULL, NULL, NULL, -1, -1, fileno(fp_err), infile, NULL, NULL, NULL,
1144 NULL, buf_string(certfile), NULL, c_smime_get_signer_cert_command);
1145 if (pid == -1)
1146 {
1147 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
1148 goto cleanup;
1149 }
1150
1151 filter_wait(pid);
1152
1153 fflush(fp_out);
1154 rewind(fp_out);
1155 fflush(fp_err);
1156 rewind(fp_err);
1157 empty = (fgetc(fp_out) == EOF);
1158 if (empty)
1159 {
1160 mutt_endwin();
1161 mutt_file_copy_stream(fp_err, stdout);
1163 goto cleanup;
1164 }
1165
1166 mutt_file_fclose(&fp_out);
1167 cert = buf_strdup(certfile);
1168
1169cleanup:
1170 mutt_file_fclose(&fp_err);
1171 if (fp_out)
1172 {
1173 mutt_file_fclose(&fp_out);
1174 mutt_file_unlink(buf_string(certfile));
1175 }
1176 buf_pool_release(&certfile);
1177 return cert;
1178}
1179
1183void smime_class_invoke_import(const char *infile, const char *mailbox)
1184{
1185 char *certfile = NULL;
1186 struct Buffer *buf = NULL;
1187
1188 FILE *fp_err = mutt_file_mkstemp();
1189 if (!fp_err)
1190 {
1191 mutt_perror(_("Can't create temporary file"));
1192 goto done;
1193 }
1194
1195 FILE *fp_out = mutt_file_mkstemp();
1196 if (!fp_out)
1197 {
1198 mutt_perror(_("Can't create temporary file"));
1199 goto done;
1200 }
1201
1202 buf = buf_pool_get();
1203 const bool c_smime_ask_cert_label = cs_subset_bool(NeoMutt->sub, "smime_ask_cert_label");
1204 if (c_smime_ask_cert_label)
1205 {
1206 if ((buf_get_field(_("Label for certificate: "), buf, MUTT_COMP_NO_FLAGS,
1207 false, NULL, NULL, NULL) != 0) ||
1208 buf_is_empty(buf))
1209 {
1210 goto done;
1211 }
1212 }
1213
1214 mutt_endwin();
1215 certfile = smime_extract_certificate(infile);
1216 if (certfile)
1217 {
1218 mutt_endwin();
1219
1220 const char *const c_smime_import_cert_command = cs_subset_string(NeoMutt->sub, "smime_import_cert_command");
1221 FILE *fp_smime_in = NULL;
1222 pid_t pid = smime_invoke(&fp_smime_in, NULL, NULL, -1, fileno(fp_out),
1223 fileno(fp_err), certfile, NULL, NULL, NULL, NULL,
1224 NULL, NULL, c_smime_import_cert_command);
1225 if (pid == -1)
1226 {
1227 mutt_message(_("Error: unable to create OpenSSL subprocess"));
1228 goto done;
1229 }
1230 fputs(buf_string(buf), fp_smime_in);
1231 fputc('\n', fp_smime_in);
1232 mutt_file_fclose(&fp_smime_in);
1233
1234 filter_wait(pid);
1235
1236 mutt_file_unlink(certfile);
1237 FREE(&certfile);
1238 }
1239
1240 fflush(fp_out);
1241 rewind(fp_out);
1242 fflush(fp_err);
1243 rewind(fp_err);
1244
1245 mutt_file_copy_stream(fp_out, stdout);
1246 mutt_file_copy_stream(fp_err, stdout);
1247
1248done:
1249 mutt_file_fclose(&fp_out);
1250 mutt_file_fclose(&fp_err);
1251 buf_pool_release(&buf);
1252}
1253
1257int smime_class_verify_sender(struct Email *e, struct Message *msg)
1258{
1259 const char *mbox = NULL, *certfile = NULL;
1260 int rc = 1;
1261
1262 struct Buffer *tempfname = buf_pool_get();
1263 buf_mktemp(tempfname);
1264 FILE *fp_out = mutt_file_fopen(buf_string(tempfname), "w");
1265 if (!fp_out)
1266 {
1267 mutt_perror(buf_string(tempfname));
1268 goto cleanup;
1269 }
1270
1271 const bool encrypt = e->security & SEC_ENCRYPT;
1272 mutt_copy_message(fp_out, e, msg,
1274 encrypt ? (CH_MIME | CH_WEED | CH_NONEWLINE) : CH_NO_FLAGS, 0);
1275
1276 fflush(fp_out);
1277 mutt_file_fclose(&fp_out);
1278
1279 if (!TAILQ_EMPTY(&e->env->from))
1280 {
1282 mbox = buf_string(TAILQ_FIRST(&e->env->from)->mailbox);
1283 }
1284 else if (!TAILQ_EMPTY(&e->env->sender))
1285 {
1287 mbox = buf_string(TAILQ_FIRST(&e->env->sender)->mailbox);
1288 }
1289
1290 if (mbox)
1291 {
1292 certfile = smime_extract_signer_certificate(buf_string(tempfname));
1293 if (certfile)
1294 {
1295 mutt_file_unlink(buf_string(tempfname));
1296 if (smime_handle_cert_email(certfile, mbox, false, NULL, NULL))
1297 {
1298 if (isendwin())
1300 }
1301 else
1302 {
1303 rc = 0;
1304 }
1305 mutt_file_unlink(certfile);
1306 FREE(&certfile);
1307 }
1308 else
1309 {
1310 mutt_any_key_to_continue(_("no certfile"));
1311 }
1312 }
1313 else
1314 {
1315 mutt_any_key_to_continue(_("no mbox"));
1316 }
1317
1318 mutt_file_unlink(buf_string(tempfname));
1319
1320cleanup:
1321 buf_pool_release(&tempfname);
1322 return rc;
1323}
1324
1341static pid_t smime_invoke_encrypt(FILE **fp_smime_in, FILE **fp_smime_out,
1342 FILE **fp_smime_err, int fp_smime_infd,
1343 int fp_smime_outfd, int fp_smime_errfd,
1344 const char *fname, const char *uids)
1345{
1346 const char *const c_smime_encrypt_with = cs_subset_string(NeoMutt->sub, "smime_encrypt_with");
1347 const char *const c_smime_encrypt_command = cs_subset_string(NeoMutt->sub, "smime_encrypt_command");
1348 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd,
1349 fp_smime_outfd, fp_smime_errfd, fname, NULL, c_smime_encrypt_with,
1350 NULL, NULL, uids, NULL, c_smime_encrypt_command);
1351}
1352
1368static pid_t smime_invoke_sign(FILE **fp_smime_in, FILE **fp_smime_out,
1369 FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd,
1370 int fp_smime_errfd, const char *fname)
1371{
1372 const char *const c_smime_sign_digest_alg = cs_subset_string(NeoMutt->sub, "smime_sign_digest_alg");
1373 const char *const c_smime_sign_command = cs_subset_string(NeoMutt->sub, "smime_sign_command");
1374 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd, fp_smime_outfd,
1375 fp_smime_errfd, fname, NULL, NULL, c_smime_sign_digest_alg,
1377 buf_string(&SmimeIntermediateToUse), c_smime_sign_command);
1378}
1379
1383struct Body *smime_class_build_smime_entity(struct Body *a, char *certlist)
1384{
1385 char buf[1024], certfile[PATH_MAX];
1386 char *cert_end = NULL;
1387 FILE *fp_smime_in = NULL, *fp_smime_err = NULL, *fp_out = NULL, *fp_tmp = NULL;
1388 struct Body *t = NULL;
1389 int err = 0, empty, off;
1390 pid_t pid;
1391
1392 struct Buffer *tempfile = buf_pool_get();
1393 struct Buffer *smime_infile = buf_pool_get();
1394
1395 buf_mktemp(tempfile);
1396 fp_out = mutt_file_fopen(buf_string(tempfile), "w+");
1397 if (!fp_out)
1398 {
1399 mutt_perror(buf_string(tempfile));
1400 goto cleanup;
1401 }
1402
1403 fp_smime_err = mutt_file_mkstemp();
1404 if (!fp_smime_err)
1405 {
1406 mutt_perror(_("Can't create temporary file"));
1407 goto cleanup;
1408 }
1409
1410 buf_mktemp(smime_infile);
1411 fp_tmp = mutt_file_fopen(buf_string(smime_infile), "w+");
1412 if (!fp_tmp)
1413 {
1414 mutt_perror(buf_string(smime_infile));
1415 goto cleanup;
1416 }
1417
1418 *certfile = '\0';
1419 for (char *cert_start = certlist; cert_start; cert_start = cert_end)
1420 {
1421 cert_end = strchr(cert_start, ' ');
1422 if (cert_end)
1423 *cert_end = '\0';
1424 if (*cert_start)
1425 {
1426 off = mutt_str_len(certfile);
1427 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
1428 snprintf(certfile + off, sizeof(certfile) - off, "%s%s/%s",
1429 (off != 0) ? " " : "", NONULL(c_smime_certificates), cert_start);
1430 }
1431 if (cert_end)
1432 *cert_end++ = ' ';
1433 }
1434
1435 /* write a MIME entity */
1436 mutt_write_mime_header(a, fp_tmp, NeoMutt->sub);
1437 fputc('\n', fp_tmp);
1438 mutt_write_mime_body(a, fp_tmp, NeoMutt->sub);
1439 mutt_file_fclose(&fp_tmp);
1440
1441 pid = smime_invoke_encrypt(&fp_smime_in, NULL, NULL, -1, fileno(fp_out),
1442 fileno(fp_smime_err), buf_string(smime_infile), certfile);
1443 if (pid == -1)
1444 {
1445 mutt_file_unlink(buf_string(smime_infile));
1446 goto cleanup;
1447 }
1448
1449 mutt_file_fclose(&fp_smime_in);
1450
1451 filter_wait(pid);
1452 mutt_file_unlink(buf_string(smime_infile));
1453
1454 fflush(fp_out);
1455 rewind(fp_out);
1456 empty = (fgetc(fp_out) == EOF);
1457 mutt_file_fclose(&fp_out);
1458
1459 fflush(fp_smime_err);
1460 rewind(fp_smime_err);
1461 while (fgets(buf, sizeof(buf) - 1, fp_smime_err))
1462 {
1463 err = 1;
1464 fputs(buf, stdout);
1465 }
1466 mutt_file_fclose(&fp_smime_err);
1467
1468 /* pause if there is any error output from SMIME */
1469 if (err)
1471
1472 if (empty)
1473 {
1474 /* fatal error while trying to encrypt message */
1475 if (err == 0)
1476 mutt_any_key_to_continue(_("No output from OpenSSL..."));
1477 mutt_file_unlink(buf_string(tempfile));
1478 goto cleanup;
1479 }
1480
1481 t = mutt_body_new();
1483 t->subtype = mutt_str_dup("pkcs7-mime");
1484 mutt_param_set(&t->parameter, "name", "smime.p7m");
1485 mutt_param_set(&t->parameter, "smime-type", "enveloped-data");
1486 t->encoding = ENC_BASE64; /* The output of OpenSSL SHOULD be binary */
1487 t->use_disp = true;
1489 t->d_filename = mutt_str_dup("smime.p7m");
1490 t->filename = buf_strdup(tempfile);
1491 t->unlink = true; /* delete after sending the message */
1492 t->parts = NULL;
1493 t->next = NULL;
1494
1495cleanup:
1496 if (fp_out)
1497 {
1498 mutt_file_fclose(&fp_out);
1499 mutt_file_unlink(buf_string(tempfile));
1500 }
1501 mutt_file_fclose(&fp_smime_err);
1502 if (fp_tmp)
1503 {
1504 mutt_file_fclose(&fp_tmp);
1505 mutt_file_unlink(buf_string(smime_infile));
1506 }
1507 buf_pool_release(&tempfile);
1508 buf_pool_release(&smime_infile);
1509
1510 return t;
1511}
1512
1525static char *openssl_md_to_smime_micalg(const char *md)
1526{
1527 if (!md)
1528 return 0;
1529
1530 char *micalg = NULL;
1531 if (mutt_istr_startswith(md, "sha"))
1532 {
1533 const size_t l = strlen(md) + 2;
1534 micalg = mutt_mem_malloc(l);
1535 snprintf(micalg, l, "sha-%s", md + 3);
1536 }
1537 else
1538 {
1539 micalg = mutt_str_dup(md);
1540 }
1541
1542 return micalg;
1543}
1544
1548struct Body *smime_class_sign_message(struct Body *a, const struct AddressList *from)
1549{
1550 struct Body *t = NULL;
1551 struct Body *retval = NULL;
1552 char buf[1024] = { 0 };
1553 struct Buffer *filetosign = NULL, *signedfile = NULL;
1554 FILE *fp_smime_in = NULL, *fp_smime_out = NULL, *fp_smime_err = NULL, *fp_sign = NULL;
1555 int err = 0;
1556 int empty = 0;
1557 pid_t pid;
1558 const char *intermediates = NULL;
1559
1560 const char *const c_smime_sign_as = cs_subset_string(NeoMutt->sub, "smime_sign_as");
1561 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
1562 const char *signas = c_smime_sign_as ? c_smime_sign_as : c_smime_default_key;
1563 if (!signas || (*signas == '\0'))
1564 {
1565 mutt_error(_("Can't sign: No key specified. Use Sign As."));
1566 return NULL;
1567 }
1568
1569 crypt_convert_to_7bit(a); /* Signed data _must_ be in 7-bit format. */
1570
1571 filetosign = buf_pool_get();
1572 signedfile = buf_pool_get();
1573
1574 buf_mktemp(filetosign);
1575 fp_sign = mutt_file_fopen(buf_string(filetosign), "w+");
1576 if (!fp_sign)
1577 {
1578 mutt_perror(buf_string(filetosign));
1579 goto cleanup;
1580 }
1581
1582 buf_mktemp(signedfile);
1583 fp_smime_out = mutt_file_fopen(buf_string(signedfile), "w+");
1584 if (!fp_smime_out)
1585 {
1586 mutt_perror(buf_string(signedfile));
1587 goto cleanup;
1588 }
1589
1590 mutt_write_mime_header(a, fp_sign, NeoMutt->sub);
1591 fputc('\n', fp_sign);
1592 mutt_write_mime_body(a, fp_sign, NeoMutt->sub);
1593 mutt_file_fclose(&fp_sign);
1594
1595 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
1596 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
1597 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), signas);
1598 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), signas);
1599
1600 struct SmimeKey *signas_key = smime_get_key_by_hash(signas, 1);
1601 if (!signas_key || mutt_str_equal("?", signas_key->issuer))
1602 intermediates = signas; /* so openssl won't complain in any case */
1603 else
1604 intermediates = signas_key->issuer;
1605
1606 buf_printf(&SmimeIntermediateToUse, "%s/%s", NONULL(c_smime_certificates), intermediates);
1607
1608 smime_key_free(&signas_key);
1609
1610 pid = smime_invoke_sign(&fp_smime_in, NULL, &fp_smime_err, -1,
1611 fileno(fp_smime_out), -1, buf_string(filetosign));
1612 if (pid == -1)
1613 {
1614 mutt_perror(_("Can't open OpenSSL subprocess"));
1615 mutt_file_unlink(buf_string(filetosign));
1616 goto cleanup;
1617 }
1618 fputs(SmimePass, fp_smime_in);
1619 fputc('\n', fp_smime_in);
1620 mutt_file_fclose(&fp_smime_in);
1621
1622 filter_wait(pid);
1623
1624 /* check for errors from OpenSSL */
1625 err = 0;
1626 fflush(fp_smime_err);
1627 rewind(fp_smime_err);
1628 while (fgets(buf, sizeof(buf) - 1, fp_smime_err))
1629 {
1630 err = 1;
1631 fputs(buf, stdout);
1632 }
1633 mutt_file_fclose(&fp_smime_err);
1634
1635 fflush(fp_smime_out);
1636 rewind(fp_smime_out);
1637 empty = (fgetc(fp_smime_out) == EOF);
1638 mutt_file_fclose(&fp_smime_out);
1639
1640 mutt_file_unlink(buf_string(filetosign));
1641
1642 if (err)
1644
1645 if (empty)
1646 {
1647 mutt_any_key_to_continue(_("No output from OpenSSL..."));
1648 mutt_file_unlink(buf_string(signedfile));
1649 goto cleanup; /* fatal error while signing */
1650 }
1651
1652 t = mutt_body_new();
1653 t->type = TYPE_MULTIPART;
1654 t->subtype = mutt_str_dup("signed");
1655 t->encoding = ENC_7BIT;
1656 t->use_disp = false;
1658
1660
1661 const char *const c_smime_sign_digest_alg = cs_subset_string(NeoMutt->sub, "smime_sign_digest_alg");
1662 char *micalg = openssl_md_to_smime_micalg(c_smime_sign_digest_alg);
1663 mutt_param_set(&t->parameter, "micalg", micalg);
1664 FREE(&micalg);
1665
1666 mutt_param_set(&t->parameter, "protocol", "application/pkcs7-signature");
1667
1668 t->parts = a;
1669 retval = t;
1670
1671 t->parts->next = mutt_body_new();
1672 t = t->parts->next;
1674 t->subtype = mutt_str_dup("pkcs7-signature");
1675 t->filename = buf_strdup(signedfile);
1676 t->d_filename = mutt_str_dup("smime.p7s");
1677 t->use_disp = true;
1679 t->encoding = ENC_BASE64;
1680 t->unlink = true; /* ok to remove this file after sending. */
1681
1682cleanup:
1683 if (fp_sign)
1684 {
1685 mutt_file_fclose(&fp_sign);
1686 mutt_file_unlink(buf_string(filetosign));
1687 }
1688 if (fp_smime_out)
1689 {
1690 mutt_file_fclose(&fp_smime_out);
1691 mutt_file_unlink(buf_string(signedfile));
1692 }
1693 buf_pool_release(&filetosign);
1694 buf_pool_release(&signedfile);
1695 return retval;
1696}
1697
1715static pid_t smime_invoke_verify(FILE **fp_smime_in, FILE **fp_smime_out,
1716 FILE **fp_smime_err, int fp_smime_infd,
1717 int fp_smime_outfd, int fp_smime_errfd,
1718 const char *fname, const char *sig_fname, int opaque)
1719{
1720 const char *const c_smime_verify_opaque_command = cs_subset_string(NeoMutt->sub, "smime_verify_opaque_command");
1721 const char *const c_smime_verify_command = cs_subset_string(NeoMutt->sub, "smime_verify_command");
1722 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd, fp_smime_outfd,
1723 fp_smime_errfd, fname, sig_fname, NULL, NULL, NULL, NULL, NULL,
1724 (opaque ? c_smime_verify_opaque_command : c_smime_verify_command));
1725}
1726
1742static pid_t smime_invoke_decrypt(FILE **fp_smime_in, FILE **fp_smime_out,
1743 FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd,
1744 int fp_smime_errfd, const char *fname)
1745{
1746 const char *const c_smime_decrypt_command = cs_subset_string(NeoMutt->sub, "smime_decrypt_command");
1747 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd,
1748 fp_smime_outfd, fp_smime_errfd, fname, NULL, NULL, NULL,
1750 NULL, c_smime_decrypt_command);
1751}
1752
1756int smime_class_verify_one(struct Body *sigbdy, struct State *state, const char *tempfile)
1757{
1758 FILE *fp = NULL, *fp_smime_out = NULL, *fp_smime_err = NULL;
1759 pid_t pid;
1760 int badsig = -1;
1761
1762 LOFF_T tmpoffset = 0;
1763 size_t tmplength = 0;
1764 int orig_type = sigbdy->type;
1765
1766 struct Buffer *signedfile = buf_pool_get();
1767
1768 buf_printf(signedfile, "%s.sig", tempfile);
1769
1770 /* decode to a tempfile, saving the original destination */
1771 fp = state->fp_out;
1772 state->fp_out = mutt_file_fopen(buf_string(signedfile), "w");
1773 if (!state->fp_out)
1774 {
1775 mutt_perror(buf_string(signedfile));
1776 goto cleanup;
1777 }
1778 /* decoding the attachment changes the size and offset, so save a copy
1779 * of the "real" values now, and restore them after processing */
1780 tmplength = sigbdy->length;
1781 tmpoffset = sigbdy->offset;
1782
1783 /* if we are decoding binary bodies, we don't want to prefix each
1784 * line with the prefix or else the data will get corrupted. */
1785 char *save_prefix = state->prefix;
1786 state->prefix = NULL;
1787
1788 mutt_decode_attachment(sigbdy, state);
1789
1790 sigbdy->length = ftello(state->fp_out);
1791 sigbdy->offset = 0;
1792 mutt_file_fclose(&state->fp_out);
1793
1794 /* restore final destination and substitute the tempfile for input */
1795 state->fp_out = fp;
1796 fp = state->fp_in;
1797 state->fp_in = fopen(buf_string(signedfile), "r");
1798
1799 /* restore the prefix */
1800 state->prefix = save_prefix;
1801
1802 sigbdy->type = orig_type;
1803
1804 fp_smime_err = mutt_file_mkstemp();
1805 if (!fp_smime_err)
1806 {
1807 mutt_perror(_("Can't create temporary file"));
1808 goto cleanup;
1809 }
1810
1811 crypt_current_time(state, "OpenSSL");
1812
1813 pid = smime_invoke_verify(NULL, &fp_smime_out, NULL, -1, -1, fileno(fp_smime_err),
1814 tempfile, buf_string(signedfile), 0);
1815 if (pid != -1)
1816 {
1817 fflush(fp_smime_out);
1818 mutt_file_fclose(&fp_smime_out);
1819
1820 if (filter_wait(pid))
1821 {
1822 badsig = -1;
1823 }
1824 else
1825 {
1826 char *line = NULL;
1827 size_t linelen;
1828
1829 fflush(fp_smime_err);
1830 rewind(fp_smime_err);
1831
1832 line = mutt_file_read_line(line, &linelen, fp_smime_err, NULL, MUTT_RL_NO_FLAGS);
1833 if (linelen && mutt_istr_equal(line, "verification successful"))
1834 badsig = 0;
1835
1836 FREE(&line);
1837 }
1838 }
1839
1840 fflush(fp_smime_err);
1841 rewind(fp_smime_err);
1842 mutt_file_copy_stream(fp_smime_err, state->fp_out);
1843 mutt_file_fclose(&fp_smime_err);
1844
1845 state_attach_puts(state, _("[-- End of OpenSSL output --]\n\n"));
1846
1847 mutt_file_unlink(buf_string(signedfile));
1848
1849 sigbdy->length = tmplength;
1850 sigbdy->offset = tmpoffset;
1851
1852 /* restore the original source stream */
1853 mutt_file_fclose(&state->fp_in);
1854 state->fp_in = fp;
1855
1856cleanup:
1857 buf_pool_release(&signedfile);
1858 return badsig;
1859}
1860
1870static struct Body *smime_handle_entity(struct Body *m, struct State *state, FILE *fp_out_file)
1871{
1872 struct Buffer tmpfname = buf_make(0);
1873 FILE *fp_smime_out = NULL, *fp_smime_in = NULL, *fp_smime_err = NULL;
1874 FILE *fp_tmp = NULL, *fp_out = NULL;
1875 struct Body *p = NULL;
1876 pid_t pid = -1;
1878
1879 if (!(type & APPLICATION_SMIME))
1880 return NULL;
1881
1882 /* Because of the mutt_body_handler() we avoid the buffer pool. */
1883 fp_smime_out = mutt_file_mkstemp();
1884 if (!fp_smime_out)
1885 {
1886 mutt_perror(_("Can't create temporary file"));
1887 goto cleanup;
1888 }
1889
1890 fp_smime_err = mutt_file_mkstemp();
1891 if (!fp_smime_err)
1892 {
1893 mutt_perror(_("Can't create temporary file"));
1894 goto cleanup;
1895 }
1896
1897 buf_mktemp(&tmpfname);
1898 fp_tmp = mutt_file_fopen(buf_string(&tmpfname), "w+");
1899 if (!fp_tmp)
1900 {
1901 mutt_perror(buf_string(&tmpfname));
1902 goto cleanup;
1903 }
1904
1905 if (!mutt_file_seek(state->fp_in, m->offset, SEEK_SET))
1906 {
1907 goto cleanup;
1908 }
1909
1910 mutt_file_copy_bytes(state->fp_in, fp_tmp, m->length);
1911
1912 fflush(fp_tmp);
1913 mutt_file_fclose(&fp_tmp);
1914
1915 if ((type & SEC_ENCRYPT) &&
1916 ((pid = smime_invoke_decrypt(&fp_smime_in, NULL, NULL, -1, fileno(fp_smime_out),
1917 fileno(fp_smime_err), buf_string(&tmpfname))) == -1))
1918 {
1919 mutt_file_unlink(buf_string(&tmpfname));
1920 if (state->flags & STATE_DISPLAY)
1921 {
1922 state_attach_puts(state, _("[-- Error: unable to create OpenSSL subprocess --]\n"));
1923 }
1924 goto cleanup;
1925 }
1926 else if ((type & SEC_SIGNOPAQUE) &&
1927 ((pid = smime_invoke_verify(&fp_smime_in, NULL, NULL, -1,
1928 fileno(fp_smime_out), fileno(fp_smime_err), NULL,
1929 buf_string(&tmpfname), SEC_SIGNOPAQUE)) == -1))
1930 {
1931 mutt_file_unlink(buf_string(&tmpfname));
1932 if (state->flags & STATE_DISPLAY)
1933 {
1934 state_attach_puts(state, _("[-- Error: unable to create OpenSSL subprocess --]\n"));
1935 }
1936 goto cleanup;
1937 }
1938
1939 if (type & SEC_ENCRYPT)
1940 {
1943 fputs(SmimePass, fp_smime_in);
1944 fputc('\n', fp_smime_in);
1945 }
1946
1947 mutt_file_fclose(&fp_smime_in);
1948
1949 filter_wait(pid);
1950 mutt_file_unlink(buf_string(&tmpfname));
1951
1952 if (state->flags & STATE_DISPLAY)
1953 {
1954 fflush(fp_smime_err);
1955 rewind(fp_smime_err);
1956
1957 const int c = fgetc(fp_smime_err);
1958 if (c != EOF)
1959 {
1960 ungetc(c, fp_smime_err);
1961
1962 crypt_current_time(state, "OpenSSL");
1963 mutt_file_copy_stream(fp_smime_err, state->fp_out);
1964 state_attach_puts(state, _("[-- End of OpenSSL output --]\n\n"));
1965 }
1966
1967 if (type & SEC_ENCRYPT)
1968 {
1969 state_attach_puts(state, _("[-- The following data is S/MIME encrypted --]\n"));
1970 }
1971 else
1972 {
1973 state_attach_puts(state, _("[-- The following data is S/MIME signed --]\n"));
1974 }
1975 }
1976
1977 fflush(fp_smime_out);
1978 rewind(fp_smime_out);
1979
1980 if (type & SEC_ENCRYPT)
1981 {
1982 /* void the passphrase, even if that wasn't the problem */
1983 if (fgetc(fp_smime_out) == EOF)
1984 {
1985 mutt_error(_("Decryption failed"));
1987 }
1988 rewind(fp_smime_out);
1989 }
1990
1991 if (fp_out_file)
1992 {
1993 fp_out = fp_out_file;
1994 }
1995 else
1996 {
1997 fp_out = mutt_file_mkstemp();
1998 if (!fp_out)
1999 {
2000 mutt_perror(_("Can't create temporary file"));
2001 goto cleanup;
2002 }
2003 }
2004 char buf[8192] = { 0 };
2005 while (fgets(buf, sizeof(buf) - 1, fp_smime_out))
2006 {
2007 const size_t len = mutt_str_len(buf);
2008 if ((len > 1) && (buf[len - 2] == '\r'))
2009 {
2010 buf[len - 2] = '\n';
2011 buf[len - 1] = '\0';
2012 }
2013 fputs(buf, fp_out);
2014 }
2015 fflush(fp_out);
2016 rewind(fp_out);
2017
2018 const long size = mutt_file_get_size_fp(fp_out);
2019 if (size == 0)
2020 {
2021 goto cleanup;
2022 }
2023 p = mutt_read_mime_header(fp_out, 0);
2024 if (p)
2025 {
2026 p->length = size - p->offset;
2027
2028 mutt_parse_part(fp_out, p);
2029
2030 if (state->flags & STATE_DISPLAY)
2032
2033 /* Store any protected headers in the parent so they can be
2034 * accessed for index updates after the handler recursion is done.
2035 * This is done before the handler to prevent a nested encrypted
2036 * handler from freeing the headers. */
2038 m->mime_headers = p->mime_headers;
2039 p->mime_headers = NULL;
2040
2041 if (state->fp_out)
2042 {
2043 rewind(fp_out);
2044 FILE *fp_tmp_buffer = state->fp_in;
2045 state->fp_in = fp_out;
2046 mutt_body_handler(p, state);
2047 state->fp_in = fp_tmp_buffer;
2048 }
2049
2050 /* Embedded multipart signed protected headers override the
2051 * encrypted headers. We need to do this after the handler so
2052 * they can be printed in the pager. */
2053 if (!(type & SMIME_SIGN) && mutt_is_multipart_signed(p) && p->parts &&
2054 p->parts->mime_headers)
2055 {
2058 p->parts->mime_headers = NULL;
2059 }
2060 }
2061 mutt_file_fclose(&fp_smime_out);
2062
2063 if (!fp_out_file)
2064 {
2065 mutt_file_fclose(&fp_out);
2066 mutt_file_unlink(buf_string(&tmpfname));
2067 }
2068 fp_out = NULL;
2069
2070 if (state->flags & STATE_DISPLAY)
2071 {
2072 if (type & SEC_ENCRYPT)
2073 state_attach_puts(state, _("\n[-- End of S/MIME encrypted data. --]\n"));
2074 else
2075 state_attach_puts(state, _("\n[-- End of S/MIME signed data. --]\n"));
2076 }
2077
2078 if (type & SEC_SIGNOPAQUE)
2079 {
2080 char *line = NULL;
2081 size_t linelen;
2082
2083 rewind(fp_smime_err);
2084
2085 line = mutt_file_read_line(line, &linelen, fp_smime_err, NULL, MUTT_RL_NO_FLAGS);
2086 if (linelen && mutt_istr_equal(line, "verification successful"))
2087 m->goodsig = true;
2088 FREE(&line);
2089 }
2090 else if (p)
2091 {
2092 m->goodsig = p->goodsig;
2093 m->badsig = p->badsig;
2094 }
2095
2096cleanup:
2097 mutt_file_fclose(&fp_smime_out);
2098 mutt_file_fclose(&fp_smime_err);
2099 mutt_file_fclose(&fp_tmp);
2100 mutt_file_fclose(&fp_out);
2101 buf_dealloc(&tmpfname);
2102 return p;
2103}
2104
2108int smime_class_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **cur)
2109{
2110 struct State state = { 0 };
2111 LOFF_T tmpoffset = b->offset;
2112 size_t tmplength = b->length;
2113 int rc = -1;
2114
2116 return -1;
2117
2118 if (b->parts)
2119 return -1;
2120
2121 state.fp_in = fp_in;
2122 if (!mutt_file_seek(state.fp_in, b->offset, SEEK_SET))
2123 {
2124 return -1;
2125 }
2126
2127 FILE *fp_tmp = mutt_file_mkstemp();
2128 if (!fp_tmp)
2129 {
2130 mutt_perror(_("Can't create temporary file"));
2131 return -1;
2132 }
2133
2134 state.fp_out = fp_tmp;
2135 mutt_decode_attachment(b, &state);
2136 fflush(fp_tmp);
2137 b->length = ftello(state.fp_out);
2138 b->offset = 0;
2139 rewind(fp_tmp);
2140 state.fp_in = fp_tmp;
2141 state.fp_out = 0;
2142
2144 if (!*fp_out)
2145 {
2146 mutt_perror(_("Can't create temporary file"));
2147 goto bail;
2148 }
2149
2150 *cur = smime_handle_entity(b, &state, *fp_out);
2151 if (!*cur)
2152 goto bail;
2153
2154 (*cur)->goodsig = b->goodsig;
2155 (*cur)->badsig = b->badsig;
2156 rc = 0;
2157
2158bail:
2159 b->length = tmplength;
2160 b->offset = tmpoffset;
2161 mutt_file_fclose(&fp_tmp);
2162 if (*fp_out)
2163 rewind(*fp_out);
2164
2165 return rc;
2166}
2167
2171int smime_class_application_handler(struct Body *m, struct State *state)
2172{
2173 int rc = -1;
2174
2175 /* clear out any mime headers before the handler, so they can't be spoofed. */
2177
2178 struct Body *tattach = smime_handle_entity(m, state, NULL);
2179 if (tattach)
2180 {
2181 rc = 0;
2182 mutt_body_free(&tattach);
2183 }
2184 return rc;
2185}
2186
2191{
2192 struct SmimeKey *key = NULL;
2193 const char *prompt = NULL;
2194 const char *letters = NULL;
2195 const char *choices = NULL;
2196 int choice;
2197
2199 return e->security;
2200
2202
2203 /* Opportunistic encrypt is controlling encryption.
2204 * NOTE: "Signing" and "Clearing" only adjust the sign bit, so we have different
2205 * letter choices for those. */
2206 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt");
2207 if (c_crypt_opportunistic_encrypt && (e->security & SEC_OPPENCRYPT))
2208 {
2209 /* L10N: S/MIME options (opportunistic encryption is on) */
2210 prompt = _("S/MIME (s)ign, encrypt (w)ith, sign (a)s, (c)lear, or (o)ppenc mode off?");
2211 /* L10N: S/MIME options (opportunistic encryption is on) */
2212 letters = _("swaco");
2213 choices = "SwaCo";
2214 }
2215 else if (c_crypt_opportunistic_encrypt)
2216 {
2217 /* Opportunistic encryption option is set, but is toggled off
2218 * for this message. */
2219 /* L10N: S/MIME options (opportunistic encryption is off) */
2220 prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, (c)lear, or (o)ppenc mode?");
2221 /* L10N: S/MIME options (opportunistic encryption is off) */
2222 letters = _("eswabco");
2223 choices = "eswabcO";
2224 }
2225 else
2226 {
2227 /* Opportunistic encryption is unset */
2228 /* L10N: S/MIME options */
2229 prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear?");
2230 /* L10N: S/MIME options */
2231 letters = _("eswabc");
2232 choices = "eswabc";
2233 }
2234
2235 choice = mutt_multi_choice(prompt, letters);
2236 if (choice > 0)
2237 {
2238 switch (choices[choice - 1])
2239 {
2240 case 'a': /* sign (a)s */
2241 key = smime_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN, false);
2242 if (key)
2243 {
2244 cs_subset_str_string_set(NeoMutt->sub, "smime_sign_as", key->hash, NULL);
2245 smime_key_free(&key);
2246
2247 e->security |= SEC_SIGN;
2248
2249 /* probably need a different passphrase */
2251 }
2252
2253 break;
2254
2255 case 'b': /* (b)oth */
2256 e->security |= (SEC_ENCRYPT | SEC_SIGN);
2257 break;
2258
2259 case 'c': /* (c)lear */
2260 e->security &= ~(SEC_ENCRYPT | SEC_SIGN);
2261 break;
2262
2263 case 'C':
2264 e->security &= ~SEC_SIGN;
2265 break;
2266
2267 case 'e': /* (e)ncrypt */
2268 e->security |= SEC_ENCRYPT;
2269 e->security &= ~SEC_SIGN;
2270 break;
2271
2272 case 'O': /* oppenc mode on */
2275 break;
2276
2277 case 'o': /* oppenc mode off */
2278 e->security &= ~SEC_OPPENCRYPT;
2279 break;
2280
2281 case 'S': /* (s)ign in oppenc mode */
2282 e->security |= SEC_SIGN;
2283 break;
2284
2285 case 's': /* (s)ign */
2286 e->security &= ~SEC_ENCRYPT;
2287 e->security |= SEC_SIGN;
2288 break;
2289
2290 case 'w': /* encrypt (w)ith */
2291 {
2292 e->security |= SEC_ENCRYPT;
2293 do
2294 {
2295 struct Buffer errmsg = buf_make(0);
2296 int rc = CSR_SUCCESS;
2297 switch (mutt_multi_choice(_("Choose algorithm family: (1) DES, (2) RC2, (3) AES, or (c)lear?"),
2298 // L10N: Options for: Choose algorithm family: (1) DES, (2) RC2, (3) AES, or (c)lear?
2299 _("123c")))
2300 {
2301 case 1:
2302 switch (choice = mutt_multi_choice(_("(1) DES, (2) Triple-DES?"),
2303 // L10N: Options for: (1) DES, (2) Triple-DES
2304 _("12")))
2305 {
2306 case 1:
2307 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2308 "des", &errmsg);
2309 break;
2310 case 2:
2311 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2312 "des3", &errmsg);
2313 break;
2314 }
2315 break;
2316
2317 case 2:
2318 switch (choice = mutt_multi_choice(_("(1) RC2-40, (2) RC2-64, (3) RC2-128?"),
2319 // L10N: Options for: (1) RC2-40, (2) RC2-64, (3) RC2-128
2320 _("123")))
2321 {
2322 case 1:
2323 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2324 "rc2-40", &errmsg);
2325 break;
2326 case 2:
2327 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2328 "rc2-64", &errmsg);
2329 break;
2330 case 3:
2331 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2332 "rc2-128", &errmsg);
2333 break;
2334 }
2335 break;
2336
2337 case 3:
2338 switch (choice = mutt_multi_choice(_("(1) AES128, (2) AES192, (3) AES256?"),
2339 // L10N: Options for: (1) AES128, (2) AES192, (3) AES256
2340 _("123")))
2341 {
2342 case 1:
2343 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2344 "aes128", &errmsg);
2345 break;
2346 case 2:
2347 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2348 "aes192", &errmsg);
2349 break;
2350 case 3:
2351 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2352 "aes256", &errmsg);
2353 break;
2354 }
2355 break;
2356
2357 case 4:
2358 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2359 NULL, &errmsg);
2360 /* (c)lear */
2361 /* fallthrough */
2362 case -1: /* Ctrl-G or Enter */
2363 choice = 0;
2364 break;
2365 }
2366
2367 if ((CSR_RESULT(rc) != CSR_SUCCESS) && !buf_is_empty(&errmsg))
2368 mutt_error("%s", buf_string(&errmsg));
2369
2370 buf_dealloc(&errmsg);
2371 } while (choice == -1);
2372 break;
2373 }
2374 }
2375 }
2376
2377 return e->security;
2378}
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition: address.c:454
Email Address Handling.
Email Aliases.
void mutt_expand_aliases(struct AddressList *al)
Expand aliases in a List of Addresses.
Definition: alias.c:297
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition: alias.c:569
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:173
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:460
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:383
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:303
struct Buffer buf_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:70
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:401
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:536
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:347
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:90
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
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:884
Duplicate the structure of an entire email.
#define CH_NONEWLINE
Don't output terminating newline after the header.
Definition: copy.h:60
#define CH_WEED
Weed the headers?
Definition: copy.h:53
#define MUTT_CM_DECODE_SMIME
Used for decoding S/MIME messages.
Definition: copy.h:46
#define CH_MIME
Ignore MIME fields.
Definition: copy.h:61
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:35
#define MUTT_CM_DECODE_CRYPT
Definition: copy.h:48
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:51
Convenience wrapper for the core headers.
void crypt_opportunistic_encrypt(struct Email *e)
Can all recipients be determined.
Definition: crypt.c:1035
SecurityFlags mutt_is_multipart_signed(struct Body *b)
Is a message signed?
Definition: crypt.c:398
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:599
void crypt_convert_to_7bit(struct Body *a)
Convert an email to 7bit encoding.
Definition: crypt.c:799
void crypt_current_time(struct State *state, const char *app_name)
Print the current time.
Definition: crypt.c:69
Signing/encryption multiplexor.
void crypt_smime_void_passphrase(void)
Wrapper for CryptModuleSpecs::void_passphrase()
Definition: cryptglue.c:413
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:388
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:355
struct SmimeKey * dlg_select_smime_key(struct SmimeKey *keys, const char *query)
Get the user to select a key.
Definition: dlg_smime.c:194
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
Structs that make up an email.
Enter a string.
int buf_get_field(const char *field, struct Buffer *buf, CompletionFlags complete, bool multiple, struct Mailbox *m, char ***files, int *numfiles)
Ask the user for a string.
Definition: window.c:180
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:97
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:260
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:911
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:738
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:634
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:230
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:150
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1556
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:708
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:194
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:39
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
pid_t filter_create_fd(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, int fdin, int fdout, int fderr)
Run a command on a pipe (optionally connect stdin/stdout)
Definition: filter.c:61
Flags to control mutt_expando_format()
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
int smime_class_application_handler(struct Body *m, struct State *state)
Implements CryptModuleSpecs::application_handler() -.
Definition: smime.c:2171
int smime_class_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **cur)
Implements CryptModuleSpecs::decrypt_mime() -.
Definition: smime.c:2108
char * smime_class_find_keys(const struct AddressList *al, bool oppenc_mode)
Implements CryptModuleSpecs::find_keys() -.
Definition: smime.c:853
SecurityFlags smime_class_send_menu(struct Email *e)
Implements CryptModuleSpecs::send_menu() -.
Definition: smime.c:2190
struct Body * smime_class_sign_message(struct Body *a, const struct AddressList *from)
Implements CryptModuleSpecs::sign_message() -.
Definition: smime.c:1548
struct Body * smime_class_build_smime_entity(struct Body *a, char *certlist)
Implements CryptModuleSpecs::smime_build_smime_entity() -.
Definition: smime.c:1383
void smime_class_getkeys(struct Envelope *env)
Implements CryptModuleSpecs::smime_getkeys() -.
Definition: smime.c:813
void smime_class_invoke_import(const char *infile, const char *mailbox)
Implements CryptModuleSpecs::smime_invoke_import() -.
Definition: smime.c:1183
int smime_class_verify_sender(struct Email *e, struct Message *msg)
Implements CryptModuleSpecs::smime_verify_sender() -.
Definition: smime.c:1257
bool smime_class_valid_passphrase(void)
Implements CryptModuleSpecs::valid_passphrase() -.
Definition: smime.c:173
int smime_class_verify_one(struct Body *sigbdy, struct State *state, const char *tempfile)
Implements CryptModuleSpecs::verify_one() -.
Definition: smime.c:1756
void smime_class_void_passphrase(void)
Implements CryptModuleSpecs::void_passphrase() -.
Definition: smime.c:164
static const char * smime_command_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, intptr_t data, MuttFormatFlags flags)
Format an SMIME command - Implements format_t -.
Definition: smime.c:223
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string -.
Definition: muttlib.c:742
int mutt_protected_headers_handler(struct Body *b, struct State *state)
Process a protected header - Implements handler_t -.
Definition: crypt.c:1107
#define mutt_error(...)
Definition: logging2.h:90
#define mutt_message(...)
Definition: logging2.h:89
#define mutt_debug(LEVEL,...)
Definition: logging2.h:87
#define mutt_perror(...)
Definition: logging2.h:91
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:1618
void mutt_decode_attachment(struct Body *b, struct State *state)
Decode an email's attachment.
Definition: handler.c:1892
Decide how to display email content.
int mutt_write_mime_header(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:758
@ 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_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
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:43
static int search(struct Menu *menu, int op)
Search a menu.
Definition: functions.c:57
@ 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:863
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:446
Convenience wrapper for the library headers.
#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:102
#define STATE_DISPLAY
Output is displayed to the user.
Definition: state.h:32
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:810
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
const char * mutt_istr_find(const char *haystack, const char *needle)
Find first occurrence of string (ignoring case)
Definition: string.c:593
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
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:653
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:240
Many unsorted constants and some structs.
#define MUTT_COMP_PASS
Password mode (no echo)
Definition: mutt.h:66
#define MUTT_COMP_UNBUFFERED
Ignore macro buffer.
Definition: mutt.h:67
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:55
#define PATH_MAX
Definition: mutt.h:41
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:73
NeoMutt Logging.
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:333
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:110
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1772
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1337
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.
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: question.c:54
#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
int mutt_write_mime_body(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition: body.c:318
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:88
static struct SmimeKey * smime_get_key_by_hash(const char *hash, bool only_public_key)
Find a key by its hash.
Definition: smime.c:588
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:1368
static char SmimePass[256]
Cached Smime Passphrase.
Definition: smime.c:79
static time_t SmimeExpTime
Unix time when SmimePass expires.
Definition: smime.c:81
static void getkeys(const char *mailbox)
Get the keys for a mailbox.
Definition: smime.c:778
static struct SmimeKey * smime_copy_key(struct SmimeKey *key)
Copy an SMIME key.
Definition: smime.c:139
static void smime_command(char *buf, size_t buflen, struct SmimeCommandContext *cctx, const char *fmt)
Format an SMIME command string.
Definition: smime.c:395
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:695
void smime_init(void)
Initialise smime globals.
Definition: smime.c:93
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:1715
void smime_cleanup(void)
Clean up smime globals.
Definition: smime.c:103
static char * smime_extract_signer_certificate(const char *infile)
Extract the signer's certificate.
Definition: smime.c:1116
static struct SmimeKey * smime_parse_key(char *buf)
Parse an SMIME key block.
Definition: smime.c:457
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:1742
static struct SmimeKey * smime_get_candidates(const char *search, bool only_public_key)
Find keys matching a string.
Definition: smime.c:540
static struct Buffer SmimeKeyToUse
Smime key to use.
Definition: smime.c:84
static char * smime_extract_certificate(const char *infile)
Extract an SMIME certificate from a file.
Definition: smime.c:1001
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:900
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:614
static struct Body * smime_handle_entity(struct Body *m, struct State *state, FILE *fp_out_file)
Handle type application/pkcs7-mime.
Definition: smime.c:1870
static struct Buffer SmimeCertToUse
Smime certificate to use.
Definition: smime.c:86
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:741
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:1341
static void smime_key_free(struct SmimeKey **keylist)
Free a list of SMIME keys.
Definition: smime.c:114
static char * openssl_md_to_smime_micalg(const char *md)
Change the algorithm names.
Definition: smime.c:1525
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 char *format)
Run an SMIME command.
Definition: smime.c:425
SMIME helper routines.
Key value store.
#define NONULL(x)
Definition: string2.h:37
#define STR_COMMAND
Enough space for a long command line.
Definition: string2.h:35
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:34
char * data
Pointer to data.
Definition: buffer.h:35
The envelope/body of an email.
Definition: email.h:37
struct Envelope * env
Envelope information.
Definition: email.h:66
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition: email.h:41
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
A local copy of an email.
Definition: mxapi.h:43
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Data for a SIME command.
Definition: smime.c:68
const char * sig_fname
s
Definition: smime.c:73
const char * intermediates
i
Definition: smime.c:75
const char * digestalg
d
Definition: smime.c:71
const char * cryptalg
a
Definition: smime.c:70
const char * key
k
Definition: smime.c:69
const char * fname
f
Definition: smime.c:72
const char * certificates
c
Definition: smime.c:74
An SIME key.
Definition: smime.h:44
KeyFlags flags
Definition: smime.h:50
char * hash
Definition: smime.h:46
struct SmimeKey * next
Definition: smime.h:51
char * issuer
Definition: smime.h:48
char * email
Definition: smime.h:45
char * label
Definition: smime.h:47
char trust
i=Invalid r=revoked e=expired u=unverified v=verified t=trusted
Definition: smime.h:49
Keep track when processing files.
Definition: state.h:47
StateFlags flags
Flags, e.g. STATE_DISPLAY.
Definition: state.h:51
FILE * fp_out
File to write to.
Definition: state.h:49
char * prefix
String to add to the beginning of each output line.
Definition: state.h:50
FILE * fp_in
File to read from.
Definition: state.h:48
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:413
#define buf_mktemp(buf)
Definition: tmp.h:37
#define mutt_file_mkstemp()
Definition: tmp.h:40