NeoMutt  2023-11-03-85-g512e01
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
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 "editor/lib.h"
51#include "history/lib.h"
52#include "question/lib.h"
53#include "send/lib.h"
54#include "copy.h"
55#include "crypt.h"
56#include "cryptglue.h"
57#include "format_flags.h"
58#include "globals.h"
59#include "handler.h"
60#include "mutt_logging.h"
61#include "muttlib.h"
62#ifdef CRYPT_BACKEND_CLASSIC_SMIME
63#include "smime.h"
64#endif
65
67static char SmimePass[256];
69static time_t SmimeExpTime = 0; /* when does the cached passphrase expire? */
70
72static struct Buffer SmimeKeyToUse = { 0 };
74static struct Buffer SmimeCertToUse = { 0 };
76static struct Buffer SmimeIntermediateToUse = { 0 };
77
81void smime_init(void)
82{
86}
87
91void smime_cleanup(void)
92{
96}
97
102static void smime_key_free(struct SmimeKey **keylist)
103{
104 if (!keylist)
105 return;
106
107 struct SmimeKey *key = NULL;
108
109 while (*keylist)
110 {
111 key = *keylist;
112 *keylist = (*keylist)->next;
113
114 FREE(&key->email);
115 FREE(&key->hash);
116 FREE(&key->label);
117 FREE(&key->issuer);
118 FREE(&key);
119 }
120}
121
127static struct SmimeKey *smime_copy_key(struct SmimeKey *key)
128{
129 if (!key)
130 return NULL;
131
132 struct SmimeKey *copy = NULL;
133
134 copy = mutt_mem_calloc(1, sizeof(struct SmimeKey));
135 copy->email = mutt_str_dup(key->email);
136 copy->hash = mutt_str_dup(key->hash);
137 copy->label = mutt_str_dup(key->label);
138 copy->issuer = mutt_str_dup(key->issuer);
139 copy->trust = key->trust;
140 copy->flags = key->flags;
141
142 return copy;
143}
144
145/*
146 * Queries and passphrase handling.
147 */
148
153{
154 memset(SmimePass, 0, sizeof(SmimePass));
155 SmimeExpTime = 0;
156}
157
162{
163 const time_t now = mutt_date_now();
164 if (now < SmimeExpTime)
165 {
166 /* Use cached copy. */
167 return true;
168 }
169
171
172 struct Buffer *buf = buf_pool_get();
173 const int rc = mw_get_field(_("Enter S/MIME passphrase:"), buf,
176 buf_pool_release(&buf);
177
178 if (rc == 0)
179 {
180 const short c_smime_timeout = cs_subset_number(NeoMutt->sub, "smime_timeout");
181 SmimeExpTime = mutt_date_add_timeout(now, c_smime_timeout);
182 return true;
183 }
184 else
185 {
186 SmimeExpTime = 0;
187 }
188
189 return false;
190}
191
192/*
193 * The OpenSSL interface
194 */
195
210static const char *smime_command_format_str(char *buf, size_t buflen, size_t col,
211 int cols, char op, const char *src,
212 const char *prec, const char *if_str,
213 const char *else_str, intptr_t data,
214 MuttFormatFlags flags)
215{
216 char fmt[128] = { 0 };
217 struct SmimeCommandContext *cctx = (struct SmimeCommandContext *) data;
218 bool optional = (flags & MUTT_FORMAT_OPTIONAL);
219
220 switch (op)
221 {
222 case 'C':
223 {
224 const char *const c_smime_ca_location = cs_subset_path(NeoMutt->sub, "smime_ca_location");
225 if (!optional)
226 {
227 struct Buffer *path = buf_pool_get();
228 struct Buffer *buf1 = buf_pool_get();
229 struct Buffer *buf2 = buf_pool_get();
230 struct stat st = { 0 };
231
232 buf_strcpy(path, c_smime_ca_location);
233 buf_expand_path(path);
234 buf_quote_filename(buf1, buf_string(path), true);
235
236 if ((stat(buf_string(path), &st) != 0) || !S_ISDIR(st.st_mode))
237 buf_printf(buf2, "-CAfile %s", buf_string(buf1));
238 else
239 buf_printf(buf2, "-CApath %s", buf_string(buf1));
240
241 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
242 snprintf(buf, buflen, fmt, buf_string(buf2));
243
244 buf_pool_release(&path);
245 buf_pool_release(&buf1);
246 buf_pool_release(&buf2);
247 }
248 else if (!c_smime_ca_location)
249 {
250 optional = false;
251 }
252 break;
253 }
254
255 case 'c':
256 { /* certificate (list) */
257 if (!optional)
258 {
259 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
260 snprintf(buf, buflen, fmt, NONULL(cctx->certificates));
261 }
262 else if (!cctx->certificates)
263 {
264 optional = false;
265 }
266 break;
267 }
268
269 case 'i':
270 { /* intermediate certificates */
271 if (!optional)
272 {
273 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
274 snprintf(buf, buflen, fmt, NONULL(cctx->intermediates));
275 }
276 else if (!cctx->intermediates)
277 {
278 optional = false;
279 }
280 break;
281 }
282
283 case 's':
284 { /* detached signature */
285 if (!optional)
286 {
287 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
288 snprintf(buf, buflen, fmt, NONULL(cctx->sig_fname));
289 }
290 else if (!cctx->sig_fname)
291 {
292 optional = false;
293 }
294 break;
295 }
296
297 case 'k':
298 { /* private key */
299 if (!optional)
300 {
301 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
302 snprintf(buf, buflen, fmt, NONULL(cctx->key));
303 }
304 else if (!cctx->key)
305 {
306 optional = false;
307 }
308 break;
309 }
310
311 case 'a':
312 { /* algorithm for encryption */
313 if (!optional)
314 {
315 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
316 snprintf(buf, buflen, fmt, NONULL(cctx->cryptalg));
317 }
318 else if (!cctx->key)
319 {
320 optional = false;
321 }
322 break;
323 }
324
325 case 'f':
326 { /* file to process */
327 if (!optional)
328 {
329 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
330 snprintf(buf, buflen, fmt, NONULL(cctx->fname));
331 }
332 else if (!cctx->fname)
333 {
334 optional = false;
335 }
336 break;
337 }
338
339 case 'd':
340 { /* algorithm for the signature message digest */
341 if (!optional)
342 {
343 snprintf(fmt, sizeof(fmt), "%%%ss", prec);
344 snprintf(buf, buflen, fmt, NONULL(cctx->digestalg));
345 }
346 else if (!cctx->key)
347 {
348 optional = false;
349 }
350 break;
351 }
352
353 default:
354 *buf = '\0';
355 break;
356 }
357
358 if (optional)
359 {
360 mutt_expando_format(buf, buflen, col, cols, if_str,
362 }
363 else if (flags & MUTT_FORMAT_OPTIONAL)
364 {
365 mutt_expando_format(buf, buflen, col, cols, else_str,
367 }
368
369 /* We return the format string, unchanged */
370 return src;
371}
372
382static void smime_command(char *buf, size_t buflen,
383 struct SmimeCommandContext *cctx, const char *fmt)
384{
385 mutt_expando_format(buf, buflen, 0, buflen, NONULL(fmt), smime_command_format_str,
386 (intptr_t) cctx, MUTT_FORMAT_NO_FLAGS);
387 mutt_debug(LL_DEBUG2, "%s\n", buf);
388}
389
412static pid_t smime_invoke(FILE **fp_smime_in, FILE **fp_smime_out, FILE **fp_smime_err,
413 int fp_smime_infd, int fp_smime_outfd, int fp_smime_errfd,
414 const char *fname, const char *sig_fname, const char *cryptalg,
415 const char *digestalg, const char *key, const char *certificates,
416 const char *intermediates, const char *format)
417{
418 struct SmimeCommandContext cctx = { 0 };
419 char cmd[STR_COMMAND] = { 0 };
420
421 if (!format || (*format == '\0'))
422 return (pid_t) -1;
423
424 cctx.fname = fname;
425 cctx.sig_fname = sig_fname;
426 cctx.key = key;
427 cctx.cryptalg = cryptalg;
428 cctx.digestalg = digestalg;
431
432 smime_command(cmd, sizeof(cmd), &cctx, format);
433
434 return filter_create_fd(cmd, fp_smime_in, fp_smime_out, fp_smime_err,
435 fp_smime_infd, fp_smime_outfd, fp_smime_errfd, EnvList);
436}
437
444static struct SmimeKey *smime_parse_key(char *buf)
445{
446 char *pend = NULL, *p = NULL;
447 int field = 0;
448
449 struct SmimeKey *key = mutt_mem_calloc(1, sizeof(struct SmimeKey));
450
451 for (p = buf; p; p = pend)
452 {
453 /* Some users manually maintain their .index file, and use a tab
454 * as a delimiter, which the old parsing code (using fscanf)
455 * happened to allow. smime_keys uses a space, so search for both. */
456 if ((pend = strchr(p, ' ')) || (pend = strchr(p, '\t')) || (pend = strchr(p, '\n')))
457 *pend++ = 0;
458
459 /* For backward compatibility, don't count consecutive delimiters
460 * as an empty field. */
461 if (*p == '\0')
462 continue;
463
464 field++;
465
466 switch (field)
467 {
468 case 1: /* mailbox */
469 key->email = mutt_str_dup(p);
470 break;
471 case 2: /* hash */
472 key->hash = mutt_str_dup(p);
473 break;
474 case 3: /* label */
475 key->label = mutt_str_dup(p);
476 break;
477 case 4: /* issuer */
478 key->issuer = mutt_str_dup(p);
479 break;
480 case 5: /* trust */
481 key->trust = *p;
482 break;
483 case 6: /* purpose */
484 while (*p)
485 {
486 switch (*p++)
487 {
488 case 'e':
490 break;
491
492 case 's':
493 key->flags |= KEYFLAG_CANSIGN;
494 break;
495 }
496 }
497 break;
498 }
499 }
500
501 /* Old index files could be missing issuer, trust, and purpose,
502 * but anything less than that is an error. */
503 if (field < 3)
504 {
505 smime_key_free(&key);
506 return NULL;
507 }
508
509 if (field < 4)
510 key->issuer = mutt_str_dup("?");
511
512 if (field < 5)
513 key->trust = 't';
514
515 if (field < 6)
517
518 return key;
519}
520
527static struct SmimeKey *smime_get_candidates(const char *search, bool only_public_key)
528{
529 char buf[1024] = { 0 };
530 struct SmimeKey *key = NULL, *results = NULL;
531 struct SmimeKey **results_end = &results;
532
533 struct Buffer *index_file = buf_pool_get();
534 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
535 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
536 buf_printf(index_file, "%s/.index",
537 only_public_key ? NONULL(c_smime_certificates) : NONULL(c_smime_keys));
538
539 FILE *fp = mutt_file_fopen(buf_string(index_file), "r");
540 if (!fp)
541 {
542 mutt_perror("%s", buf_string(index_file));
543 buf_pool_release(&index_file);
544 return NULL;
545 }
546 buf_pool_release(&index_file);
547
548 while (fgets(buf, sizeof(buf), fp))
549 {
550 if (((*search == '\0')) || mutt_istr_find(buf, search))
551 {
552 key = smime_parse_key(buf);
553 if (key)
554 {
555 *results_end = key;
556 results_end = &key->next;
557 }
558 }
559 }
560
561 mutt_file_fclose(&fp);
562
563 return results;
564}
565
575static struct SmimeKey *smime_get_key_by_hash(const char *hash, bool only_public_key)
576{
577 struct SmimeKey *match = NULL;
578 struct SmimeKey *results = smime_get_candidates(hash, only_public_key);
579 for (struct SmimeKey *result = results; result; result = result->next)
580 {
581 if (mutt_istr_equal(hash, result->hash))
582 {
583 match = smime_copy_key(result);
584 break;
585 }
586 }
587
588 smime_key_free(&results);
589
590 return match;
591}
592
601static struct SmimeKey *smime_get_key_by_addr(const char *mailbox, KeyFlags abilities,
602 bool only_public_key, bool oppenc_mode)
603{
604 if (!mailbox)
605 return NULL;
606
607 struct SmimeKey *results = NULL, *result = NULL;
608 struct SmimeKey *matches = NULL;
609 struct SmimeKey **matches_end = &matches;
610 struct SmimeKey *match = NULL;
611 struct SmimeKey *trusted_match = NULL;
612 struct SmimeKey *valid_match = NULL;
613 struct SmimeKey *return_key = NULL;
614 bool multi_trusted_matches = false;
615
616 results = smime_get_candidates(mailbox, only_public_key);
617 for (result = results; result; result = result->next)
618 {
619 if (abilities && !(result->flags & abilities))
620 {
621 continue;
622 }
623
624 if (mutt_istr_equal(mailbox, result->email))
625 {
626 match = smime_copy_key(result);
627 *matches_end = match;
628 matches_end = &match->next;
629
630 if (match->trust == 't')
631 {
632 if (trusted_match && !mutt_istr_equal(match->hash, trusted_match->hash))
633 {
634 multi_trusted_matches = true;
635 }
636 trusted_match = match;
637 }
638 else if ((match->trust == 'u') || (match->trust == 'v'))
639 {
640 valid_match = match;
641 }
642 }
643 }
644
645 smime_key_free(&results);
646
647 if (matches)
648 {
649 if (oppenc_mode)
650 {
651 const bool c_crypt_opportunistic_encrypt_strong_keys =
652 cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt_strong_keys");
653 if (trusted_match)
654 return_key = smime_copy_key(trusted_match);
655 else if (valid_match && !c_crypt_opportunistic_encrypt_strong_keys)
656 return_key = smime_copy_key(valid_match);
657 else
658 return_key = NULL;
659 }
660 else if (trusted_match && !multi_trusted_matches)
661 {
662 return_key = smime_copy_key(trusted_match);
663 }
664 else
665 {
666 return_key = smime_copy_key(dlg_smime(matches, mailbox));
667 }
668
669 smime_key_free(&matches);
670 }
671
672 return return_key;
673}
674
682static struct SmimeKey *smime_get_key_by_str(const char *str, KeyFlags abilities, bool only_public_key)
683{
684 if (!str)
685 return NULL;
686
687 struct SmimeKey *results = NULL, *result = NULL;
688 struct SmimeKey *matches = NULL;
689 struct SmimeKey **matches_end = &matches;
690 struct SmimeKey *match = NULL;
691 struct SmimeKey *return_key = NULL;
692
693 results = smime_get_candidates(str, only_public_key);
694 for (result = results; result; result = result->next)
695 {
696 if (abilities && !(result->flags & abilities))
697 {
698 continue;
699 }
700
701 if (mutt_istr_equal(str, result->hash) ||
702 mutt_istr_find(result->email, str) || mutt_istr_find(result->label, str))
703 {
704 match = smime_copy_key(result);
705 *matches_end = match;
706 matches_end = &match->next;
707 }
708 }
709
710 smime_key_free(&results);
711
712 if (matches)
713 {
714 return_key = smime_copy_key(dlg_smime(matches, str));
715 smime_key_free(&matches);
716 }
717
718 return return_key;
719}
720
728static struct SmimeKey *smime_ask_for_key(char *prompt, KeyFlags abilities, bool only_public_key)
729{
730 struct SmimeKey *key = NULL;
731 struct Buffer *resp = buf_pool_get();
732
733 if (!prompt)
734 prompt = _("Enter keyID: ");
735
737
738 while (true)
739 {
740 buf_reset(resp);
741 if (mw_get_field(prompt, resp, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0)
742 {
743 goto done;
744 }
745
746 key = smime_get_key_by_str(buf_string(resp), abilities, only_public_key);
747 if (key)
748 goto done;
749
750 mutt_error(_("No matching keys found for \"%s\""), buf_string(resp));
751 }
752
753done:
754 buf_pool_release(&resp);
755 return key;
756}
757
765static void getkeys(const char *mailbox)
766{
767 const char *k = NULL;
768
769 struct SmimeKey *key = smime_get_key_by_addr(mailbox, KEYFLAG_CANENCRYPT, false, false);
770
771 if (!key)
772 {
773 char buf[256] = { 0 };
774 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), mailbox);
775 key = smime_ask_for_key(buf, KEYFLAG_CANENCRYPT, false);
776 }
777
778 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
779 size_t smime_keys_len = mutt_str_len(c_smime_keys);
780
781 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
782 k = key ? key->hash : NONULL(c_smime_default_key);
783
784 /* if the key is different from last time */
785 if ((buf_len(&SmimeKeyToUse) <= smime_keys_len) ||
786 !mutt_istr_equal(k, SmimeKeyToUse.data + smime_keys_len + 1))
787 {
789 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), k);
790 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
791 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), k);
792 }
793
794 smime_key_free(&key);
795}
796
801{
802 const bool c_smime_decrypt_use_default_key = cs_subset_bool(NeoMutt->sub, "smime_decrypt_use_default_key");
803 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
804 if (c_smime_decrypt_use_default_key && c_smime_default_key)
805 {
806 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
807 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), c_smime_default_key);
808 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
809 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), c_smime_default_key);
810 return;
811 }
812
813 struct Address *a = NULL;
814 TAILQ_FOREACH(a, &env->to, entries)
815 {
816 if (mutt_addr_is_user(a))
817 {
819 return;
820 }
821 }
822
823 TAILQ_FOREACH(a, &env->cc, entries)
824 {
825 if (mutt_addr_is_user(a))
826 {
828 return;
829 }
830 }
831
832 struct Address *f = mutt_default_from(NeoMutt->sub);
834 mutt_addr_free(&f);
835}
836
840char *smime_class_find_keys(const struct AddressList *al, bool oppenc_mode)
841{
842 struct SmimeKey *key = NULL;
843 char *keyid = NULL, *keylist = NULL;
844 size_t keylist_size = 0;
845 size_t keylist_used = 0;
846
847 struct Address *a = NULL;
848 TAILQ_FOREACH(a, al, entries)
849 {
850 key = smime_get_key_by_addr(buf_string(a->mailbox), KEYFLAG_CANENCRYPT, true, oppenc_mode);
851 if (!key && !oppenc_mode)
852 {
853 char buf[1024] = { 0 };
854 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), buf_string(a->mailbox));
855 key = smime_ask_for_key(buf, KEYFLAG_CANENCRYPT, true);
856 }
857 if (!key)
858 {
859 if (!oppenc_mode)
860 mutt_message(_("No (valid) certificate found for %s"), buf_string(a->mailbox));
861 FREE(&keylist);
862 return NULL;
863 }
864
865 keyid = key->hash;
866 keylist_size += mutt_str_len(keyid) + 2;
867 mutt_mem_realloc(&keylist, keylist_size);
868 sprintf(keylist + keylist_used, "%s%s", keylist_used ? " " : "", keyid);
869 keylist_used = mutt_str_len(keylist);
870
871 smime_key_free(&key);
872 }
873 return keylist;
874}
875
887static int smime_handle_cert_email(const char *certificate, const char *mailbox,
888 bool copy, char ***buffer, int *num)
889{
890 char email[256] = { 0 };
891 int rc = -1, count = 0;
892 pid_t pid;
893
894 FILE *fp_err = mutt_file_mkstemp();
895 if (!fp_err)
896 {
897 mutt_perror(_("Can't create temporary file"));
898 return 1;
899 }
900
901 FILE *fp_out = mutt_file_mkstemp();
902 if (!fp_out)
903 {
904 mutt_file_fclose(&fp_err);
905 mutt_perror(_("Can't create temporary file"));
906 return 1;
907 }
908
909 const char *const c_smime_get_cert_email_command = cs_subset_string(NeoMutt->sub, "smime_get_cert_email_command");
910 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_out), fileno(fp_err), certificate,
911 NULL, NULL, NULL, NULL, NULL, NULL, c_smime_get_cert_email_command);
912 if (pid == -1)
913 {
914 mutt_message(_("Error: unable to create OpenSSL subprocess"));
915 mutt_file_fclose(&fp_err);
916 mutt_file_fclose(&fp_out);
917 return 1;
918 }
919
920 filter_wait(pid);
921
922 fflush(fp_out);
923 rewind(fp_out);
924 fflush(fp_err);
925 rewind(fp_err);
926
927 while ((fgets(email, sizeof(email), fp_out)))
928 {
929 size_t len = mutt_str_len(email);
930 if (len && (email[len - 1] == '\n'))
931 email[len - 1] = '\0';
932 if (mutt_istr_startswith(email, mailbox))
933 rc = 1;
934
935 rc = (rc < 0) ? 0 : rc;
936 count++;
937 }
938
939 if (rc == -1)
940 {
941 mutt_endwin();
942 mutt_file_copy_stream(fp_err, stdout);
943 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
944 rc = 1;
945 }
946 else if (rc == 0)
947 {
948 rc = 1;
949 }
950 else
951 {
952 rc = 0;
953 }
954
955 if (copy && buffer && num)
956 {
957 (*num) = count;
958 *buffer = mutt_mem_calloc(count, sizeof(char *));
959 count = 0;
960
961 rewind(fp_out);
962 while ((fgets(email, sizeof(email), fp_out)))
963 {
964 size_t len = mutt_str_len(email);
965 if (len && (email[len - 1] == '\n'))
966 email[len - 1] = '\0';
967 (*buffer)[count] = mutt_mem_calloc(mutt_str_len(email) + 1, sizeof(char));
968 strncpy((*buffer)[count], email, mutt_str_len(email));
969 count++;
970 }
971 }
972 else if (copy)
973 {
974 rc = 2;
975 }
976
977 mutt_file_fclose(&fp_out);
978 mutt_file_fclose(&fp_err);
979
980 return rc;
981}
982
988static char *smime_extract_certificate(const char *infile)
989{
990 FILE *fp_err = NULL;
991 FILE *fp_out = NULL;
992 FILE *fp_cert = NULL;
993 char *rc = NULL;
994 pid_t pid;
995 int empty;
996
997 struct Buffer *pk7out = buf_pool_get();
998 struct Buffer *certfile = buf_pool_get();
999
1000 fp_err = mutt_file_mkstemp();
1001 if (!fp_err)
1002 {
1003 mutt_perror(_("Can't create temporary file"));
1004 goto cleanup;
1005 }
1006
1007 buf_mktemp(pk7out);
1008 fp_out = mutt_file_fopen(buf_string(pk7out), "w+");
1009 if (!fp_out)
1010 {
1011 mutt_perror("%s", buf_string(pk7out));
1012 goto cleanup;
1013 }
1014
1015 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
1016 * extract the full set of certificates directly. */
1017 const char *const c_smime_pk7out_command = cs_subset_string(NeoMutt->sub, "smime_pk7out_command");
1018 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_out), fileno(fp_err), infile,
1019 NULL, NULL, NULL, NULL, NULL, NULL, c_smime_pk7out_command);
1020 if (pid == -1)
1021 {
1022 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
1023 goto cleanup;
1024 }
1025
1026 filter_wait(pid);
1027
1028 fflush(fp_out);
1029 rewind(fp_out);
1030 fflush(fp_err);
1031 rewind(fp_err);
1032 empty = (fgetc(fp_out) == EOF);
1033 if (empty)
1034 {
1035 mutt_perror("%s", buf_string(pk7out));
1036 mutt_file_copy_stream(fp_err, stdout);
1037 goto cleanup;
1038 }
1039 mutt_file_fclose(&fp_out);
1040
1041 buf_mktemp(certfile);
1042 fp_cert = mutt_file_fopen(buf_string(certfile), "w+");
1043 if (!fp_cert)
1044 {
1045 mutt_perror("%s", buf_string(certfile));
1047 goto cleanup;
1048 }
1049
1050 // Step 2: Extract the certificates from a PKCS#7 structure.
1051 const char *const c_smime_get_cert_command = cs_subset_string(NeoMutt->sub, "smime_get_cert_command");
1052 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_cert), fileno(fp_err),
1053 buf_string(pk7out), NULL, NULL, NULL, NULL, NULL, NULL,
1054 c_smime_get_cert_command);
1055 if (pid == -1)
1056 {
1057 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
1059 goto cleanup;
1060 }
1061
1062 filter_wait(pid);
1063
1065
1066 fflush(fp_cert);
1067 rewind(fp_cert);
1068 fflush(fp_err);
1069 rewind(fp_err);
1070 empty = (fgetc(fp_cert) == EOF);
1071 if (empty)
1072 {
1073 mutt_file_copy_stream(fp_err, stdout);
1074 goto cleanup;
1075 }
1076
1077 mutt_file_fclose(&fp_cert);
1078
1079 rc = buf_strdup(certfile);
1080
1081cleanup:
1082 mutt_file_fclose(&fp_err);
1083 if (fp_out)
1084 {
1085 mutt_file_fclose(&fp_out);
1087 }
1088 if (fp_cert)
1089 {
1090 mutt_file_fclose(&fp_cert);
1091 mutt_file_unlink(buf_string(certfile));
1092 }
1093 buf_pool_release(&pk7out);
1094 buf_pool_release(&certfile);
1095 return rc;
1096}
1097
1103static char *smime_extract_signer_certificate(const char *infile)
1104{
1105 char *cert = NULL;
1106 struct Buffer *certfile = NULL;
1107 pid_t pid;
1108 int empty;
1109
1110 FILE *fp_err = mutt_file_mkstemp();
1111 if (!fp_err)
1112 {
1113 mutt_perror(_("Can't create temporary file"));
1114 return NULL;
1115 }
1116
1117 certfile = buf_pool_get();
1118 buf_mktemp(certfile);
1119 FILE *fp_out = mutt_file_fopen(buf_string(certfile), "w+");
1120 if (!fp_out)
1121 {
1122 mutt_file_fclose(&fp_err);
1123 mutt_perror("%s", buf_string(certfile));
1124 goto cleanup;
1125 }
1126
1127 /* Extract signer's certificate
1128 */
1129 const char *const c_smime_get_signer_cert_command = cs_subset_string(NeoMutt->sub, "smime_get_signer_cert_command");
1130 pid = smime_invoke(NULL, NULL, NULL, -1, -1, fileno(fp_err), infile, NULL, NULL, NULL,
1131 NULL, buf_string(certfile), NULL, c_smime_get_signer_cert_command);
1132 if (pid == -1)
1133 {
1134 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
1135 goto cleanup;
1136 }
1137
1138 filter_wait(pid);
1139
1140 fflush(fp_out);
1141 rewind(fp_out);
1142 fflush(fp_err);
1143 rewind(fp_err);
1144 empty = (fgetc(fp_out) == EOF);
1145 if (empty)
1146 {
1147 mutt_endwin();
1148 mutt_file_copy_stream(fp_err, stdout);
1150 goto cleanup;
1151 }
1152
1153 mutt_file_fclose(&fp_out);
1154 cert = buf_strdup(certfile);
1155
1156cleanup:
1157 mutt_file_fclose(&fp_err);
1158 if (fp_out)
1159 {
1160 mutt_file_fclose(&fp_out);
1161 mutt_file_unlink(buf_string(certfile));
1162 }
1163 buf_pool_release(&certfile);
1164 return cert;
1165}
1166
1170void smime_class_invoke_import(const char *infile, const char *mailbox)
1171{
1172 char *certfile = NULL;
1173 struct Buffer *buf = NULL;
1174
1175 FILE *fp_out = NULL;
1176 FILE *fp_err = mutt_file_mkstemp();
1177 if (!fp_err)
1178 {
1179 mutt_perror(_("Can't create temporary file"));
1180 goto done;
1181 }
1182
1183 fp_out = mutt_file_mkstemp();
1184 if (!fp_out)
1185 {
1186 mutt_perror(_("Can't create temporary file"));
1187 goto done;
1188 }
1189
1190 buf = buf_pool_get();
1191 const bool c_smime_ask_cert_label = cs_subset_bool(NeoMutt->sub, "smime_ask_cert_label");
1192 if (c_smime_ask_cert_label)
1193 {
1194 if ((mw_get_field(_("Label for certificate: "), buf, MUTT_COMP_NO_FLAGS,
1195 HC_OTHER, NULL, NULL) != 0) ||
1196 buf_is_empty(buf))
1197 {
1198 goto done;
1199 }
1200 }
1201
1202 mutt_endwin();
1203 certfile = smime_extract_certificate(infile);
1204 if (certfile)
1205 {
1206 mutt_endwin();
1207
1208 const char *const c_smime_import_cert_command = cs_subset_string(NeoMutt->sub, "smime_import_cert_command");
1209 FILE *fp_smime_in = NULL;
1210 pid_t pid = smime_invoke(&fp_smime_in, NULL, NULL, -1, fileno(fp_out),
1211 fileno(fp_err), certfile, NULL, NULL, NULL, NULL,
1212 NULL, NULL, c_smime_import_cert_command);
1213 if (pid == -1)
1214 {
1215 mutt_message(_("Error: unable to create OpenSSL subprocess"));
1216 goto done;
1217 }
1218 fputs(buf_string(buf), fp_smime_in);
1219 fputc('\n', fp_smime_in);
1220 mutt_file_fclose(&fp_smime_in);
1221
1222 filter_wait(pid);
1223
1224 mutt_file_unlink(certfile);
1225 FREE(&certfile);
1226 }
1227
1228 fflush(fp_out);
1229 rewind(fp_out);
1230 fflush(fp_err);
1231 rewind(fp_err);
1232
1233 mutt_file_copy_stream(fp_out, stdout);
1234 mutt_file_copy_stream(fp_err, stdout);
1235
1236done:
1237 mutt_file_fclose(&fp_out);
1238 mutt_file_fclose(&fp_err);
1239 buf_pool_release(&buf);
1240}
1241
1245int smime_class_verify_sender(struct Email *e, struct Message *msg)
1246{
1247 const char *mbox = NULL, *certfile = NULL;
1248 int rc = 1;
1249
1250 struct Buffer *tempfname = buf_pool_get();
1251 buf_mktemp(tempfname);
1252 FILE *fp_out = mutt_file_fopen(buf_string(tempfname), "w");
1253 if (!fp_out)
1254 {
1255 mutt_perror("%s", buf_string(tempfname));
1256 goto cleanup;
1257 }
1258
1259 const bool encrypt = e->security & SEC_ENCRYPT;
1260 mutt_copy_message(fp_out, e, msg,
1262 encrypt ? (CH_MIME | CH_WEED | CH_NONEWLINE) : CH_NO_FLAGS, 0);
1263
1264 fflush(fp_out);
1265 mutt_file_fclose(&fp_out);
1266
1267 if (!TAILQ_EMPTY(&e->env->from))
1268 {
1270 mbox = buf_string(TAILQ_FIRST(&e->env->from)->mailbox);
1271 }
1272 else if (!TAILQ_EMPTY(&e->env->sender))
1273 {
1275 mbox = buf_string(TAILQ_FIRST(&e->env->sender)->mailbox);
1276 }
1277
1278 if (mbox)
1279 {
1280 certfile = smime_extract_signer_certificate(buf_string(tempfname));
1281 if (certfile)
1282 {
1283 mutt_file_unlink(buf_string(tempfname));
1284 if (smime_handle_cert_email(certfile, mbox, false, NULL, NULL))
1285 {
1286 if (isendwin())
1288 }
1289 else
1290 {
1291 rc = 0;
1292 }
1293 mutt_file_unlink(certfile);
1294 FREE(&certfile);
1295 }
1296 else
1297 {
1298 mutt_any_key_to_continue(_("no certfile"));
1299 }
1300 }
1301 else
1302 {
1303 mutt_any_key_to_continue(_("no mbox"));
1304 }
1305
1306 mutt_file_unlink(buf_string(tempfname));
1307
1308cleanup:
1309 buf_pool_release(&tempfname);
1310 return rc;
1311}
1312
1329static pid_t smime_invoke_encrypt(FILE **fp_smime_in, FILE **fp_smime_out,
1330 FILE **fp_smime_err, int fp_smime_infd,
1331 int fp_smime_outfd, int fp_smime_errfd,
1332 const char *fname, const char *uids)
1333{
1334 const char *const c_smime_encrypt_with = cs_subset_string(NeoMutt->sub, "smime_encrypt_with");
1335 const char *const c_smime_encrypt_command = cs_subset_string(NeoMutt->sub, "smime_encrypt_command");
1336 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd,
1337 fp_smime_outfd, fp_smime_errfd, fname, NULL, c_smime_encrypt_with,
1338 NULL, NULL, uids, NULL, c_smime_encrypt_command);
1339}
1340
1356static pid_t smime_invoke_sign(FILE **fp_smime_in, FILE **fp_smime_out,
1357 FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd,
1358 int fp_smime_errfd, const char *fname)
1359{
1360 const char *const c_smime_sign_digest_alg = cs_subset_string(NeoMutt->sub, "smime_sign_digest_alg");
1361 const char *const c_smime_sign_command = cs_subset_string(NeoMutt->sub, "smime_sign_command");
1362 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd, fp_smime_outfd,
1363 fp_smime_errfd, fname, NULL, NULL, c_smime_sign_digest_alg,
1365 buf_string(&SmimeIntermediateToUse), c_smime_sign_command);
1366}
1367
1371struct Body *smime_class_build_smime_entity(struct Body *b, char *certlist)
1372{
1373 char buf[1024], certfile[PATH_MAX];
1374 char *cert_end = NULL;
1375 FILE *fp_smime_in = NULL, *fp_smime_err = NULL, *fp_out = NULL, *fp_tmp = NULL;
1376 struct Body *b_enc = NULL;
1377 int err = 0, empty, off;
1378 pid_t pid;
1379
1380 struct Buffer *tempfile = buf_pool_get();
1381 struct Buffer *smime_infile = buf_pool_get();
1382
1383 buf_mktemp(tempfile);
1384 fp_out = mutt_file_fopen(buf_string(tempfile), "w+");
1385 if (!fp_out)
1386 {
1387 mutt_perror("%s", buf_string(tempfile));
1388 goto cleanup;
1389 }
1390
1391 fp_smime_err = mutt_file_mkstemp();
1392 if (!fp_smime_err)
1393 {
1394 mutt_perror(_("Can't create temporary file"));
1395 goto cleanup;
1396 }
1397
1398 buf_mktemp(smime_infile);
1399 fp_tmp = mutt_file_fopen(buf_string(smime_infile), "w+");
1400 if (!fp_tmp)
1401 {
1402 mutt_perror("%s", buf_string(smime_infile));
1403 goto cleanup;
1404 }
1405
1406 *certfile = '\0';
1407 for (char *cert_start = certlist; cert_start; cert_start = cert_end)
1408 {
1409 cert_end = strchr(cert_start, ' ');
1410 if (cert_end)
1411 *cert_end = '\0';
1412 if (*cert_start)
1413 {
1414 off = mutt_str_len(certfile);
1415 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
1416 snprintf(certfile + off, sizeof(certfile) - off, "%s%s/%s",
1417 (off != 0) ? " " : "", NONULL(c_smime_certificates), cert_start);
1418 }
1419 if (cert_end)
1420 *cert_end++ = ' ';
1421 }
1422
1423 /* write a MIME entity */
1424 mutt_write_mime_header(b, fp_tmp, NeoMutt->sub);
1425 fputc('\n', fp_tmp);
1426 mutt_write_mime_body(b, fp_tmp, NeoMutt->sub);
1427 mutt_file_fclose(&fp_tmp);
1428
1429 pid = smime_invoke_encrypt(&fp_smime_in, NULL, NULL, -1, fileno(fp_out),
1430 fileno(fp_smime_err), buf_string(smime_infile), certfile);
1431 if (pid == -1)
1432 {
1433 mutt_file_unlink(buf_string(smime_infile));
1434 goto cleanup;
1435 }
1436
1437 mutt_file_fclose(&fp_smime_in);
1438
1439 filter_wait(pid);
1440 mutt_file_unlink(buf_string(smime_infile));
1441
1442 fflush(fp_out);
1443 rewind(fp_out);
1444 empty = (fgetc(fp_out) == EOF);
1445 mutt_file_fclose(&fp_out);
1446
1447 fflush(fp_smime_err);
1448 rewind(fp_smime_err);
1449 while (fgets(buf, sizeof(buf) - 1, fp_smime_err))
1450 {
1451 err = 1;
1452 fputs(buf, stdout);
1453 }
1454 mutt_file_fclose(&fp_smime_err);
1455
1456 /* pause if there is any error output from SMIME */
1457 if (err)
1459
1460 if (empty)
1461 {
1462 /* fatal error while trying to encrypt message */
1463 if (err == 0)
1464 mutt_any_key_to_continue(_("No output from OpenSSL..."));
1465 mutt_file_unlink(buf_string(tempfile));
1466 goto cleanup;
1467 }
1468
1469 b_enc = mutt_body_new();
1470 b_enc->type = TYPE_APPLICATION;
1471 b_enc->subtype = mutt_str_dup("pkcs7-mime");
1472 mutt_param_set(&b_enc->parameter, "name", "smime.p7m");
1473 mutt_param_set(&b_enc->parameter, "smime-type", "enveloped-data");
1474 b_enc->encoding = ENC_BASE64; /* The output of OpenSSL SHOULD be binary */
1475 b_enc->use_disp = true;
1476 b_enc->disposition = DISP_ATTACH;
1477 b_enc->d_filename = mutt_str_dup("smime.p7m");
1478 b_enc->filename = buf_strdup(tempfile);
1479 b_enc->unlink = true; /* delete after sending the message */
1480 b_enc->parts = NULL;
1481 b_enc->next = NULL;
1482
1483cleanup:
1484 if (fp_out)
1485 {
1486 mutt_file_fclose(&fp_out);
1487 mutt_file_unlink(buf_string(tempfile));
1488 }
1489 mutt_file_fclose(&fp_smime_err);
1490 if (fp_tmp)
1491 {
1492 mutt_file_fclose(&fp_tmp);
1493 mutt_file_unlink(buf_string(smime_infile));
1494 }
1495 buf_pool_release(&tempfile);
1496 buf_pool_release(&smime_infile);
1497
1498 return b_enc;
1499}
1500
1513static char *openssl_md_to_smime_micalg(const char *md)
1514{
1515 if (!md)
1516 return NULL;
1517
1518 char *micalg = NULL;
1519 if (mutt_istr_startswith(md, "sha"))
1520 {
1521 mutt_str_asprintf(&micalg, "sha-%s", md + 3);
1522 }
1523 else
1524 {
1525 micalg = mutt_str_dup(md);
1526 }
1527
1528 return micalg;
1529}
1530
1534struct Body *smime_class_sign_message(struct Body *b, const struct AddressList *from)
1535{
1536 struct Body *b_sign = NULL;
1537 struct Body *rc = NULL;
1538 char buf[1024] = { 0 };
1539 struct Buffer *filetosign = NULL, *signedfile = NULL;
1540 FILE *fp_smime_in = NULL, *fp_smime_out = NULL, *fp_smime_err = NULL, *fp_sign = NULL;
1541 int err = 0;
1542 int empty = 0;
1543 pid_t pid;
1544 const char *intermediates = NULL;
1545
1546 const char *const c_smime_sign_as = cs_subset_string(NeoMutt->sub, "smime_sign_as");
1547 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
1548 const char *signas = c_smime_sign_as ? c_smime_sign_as : c_smime_default_key;
1549 if (!signas || (*signas == '\0'))
1550 {
1551 mutt_error(_("Can't sign: No key specified. Use Sign As."));
1552 return NULL;
1553 }
1554
1555 crypt_convert_to_7bit(b); /* Signed data _must_ be in 7-bit format. */
1556
1557 filetosign = buf_pool_get();
1558 signedfile = buf_pool_get();
1559
1560 buf_mktemp(filetosign);
1561 fp_sign = mutt_file_fopen(buf_string(filetosign), "w+");
1562 if (!fp_sign)
1563 {
1564 mutt_perror("%s", buf_string(filetosign));
1565 goto cleanup;
1566 }
1567
1568 buf_mktemp(signedfile);
1569 fp_smime_out = mutt_file_fopen(buf_string(signedfile), "w+");
1570 if (!fp_smime_out)
1571 {
1572 mutt_perror("%s", buf_string(signedfile));
1573 goto cleanup;
1574 }
1575
1576 mutt_write_mime_header(b, fp_sign, NeoMutt->sub);
1577 fputc('\n', fp_sign);
1578 mutt_write_mime_body(b, fp_sign, NeoMutt->sub);
1579 mutt_file_fclose(&fp_sign);
1580
1581 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
1582 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
1583 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), signas);
1584 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), signas);
1585
1586 struct SmimeKey *signas_key = smime_get_key_by_hash(signas, 1);
1587 if (!signas_key || mutt_str_equal("?", signas_key->issuer))
1588 intermediates = signas; /* so openssl won't complain in any case */
1589 else
1590 intermediates = signas_key->issuer;
1591
1592 buf_printf(&SmimeIntermediateToUse, "%s/%s", NONULL(c_smime_certificates), intermediates);
1593
1594 smime_key_free(&signas_key);
1595
1596 pid = smime_invoke_sign(&fp_smime_in, NULL, &fp_smime_err, -1,
1597 fileno(fp_smime_out), -1, buf_string(filetosign));
1598 if (pid == -1)
1599 {
1600 mutt_perror(_("Can't open OpenSSL subprocess"));
1601 mutt_file_unlink(buf_string(filetosign));
1602 goto cleanup;
1603 }
1604 fputs(SmimePass, fp_smime_in);
1605 fputc('\n', fp_smime_in);
1606 mutt_file_fclose(&fp_smime_in);
1607
1608 filter_wait(pid);
1609
1610 /* check for errors from OpenSSL */
1611 err = 0;
1612 fflush(fp_smime_err);
1613 rewind(fp_smime_err);
1614 while (fgets(buf, sizeof(buf) - 1, fp_smime_err))
1615 {
1616 err = 1;
1617 fputs(buf, stdout);
1618 }
1619 mutt_file_fclose(&fp_smime_err);
1620
1621 fflush(fp_smime_out);
1622 rewind(fp_smime_out);
1623 empty = (fgetc(fp_smime_out) == EOF);
1624 mutt_file_fclose(&fp_smime_out);
1625
1626 mutt_file_unlink(buf_string(filetosign));
1627
1628 if (err)
1630
1631 if (empty)
1632 {
1633 mutt_any_key_to_continue(_("No output from OpenSSL..."));
1634 mutt_file_unlink(buf_string(signedfile));
1635 goto cleanup; /* fatal error while signing */
1636 }
1637
1638 b_sign = mutt_body_new();
1639 b_sign->type = TYPE_MULTIPART;
1640 b_sign->subtype = mutt_str_dup("signed");
1641 b_sign->encoding = ENC_7BIT;
1642 b_sign->use_disp = false;
1643 b_sign->disposition = DISP_INLINE;
1644
1646
1647 const char *const c_smime_sign_digest_alg = cs_subset_string(NeoMutt->sub, "smime_sign_digest_alg");
1648 char *micalg = openssl_md_to_smime_micalg(c_smime_sign_digest_alg);
1649 mutt_param_set(&b_sign->parameter, "micalg", micalg);
1650 FREE(&micalg);
1651
1652 mutt_param_set(&b_sign->parameter, "protocol", "application/pkcs7-signature");
1653
1654 b_sign->parts = b;
1655 rc = b_sign;
1656
1657 b_sign->parts->next = mutt_body_new();
1658 b_sign = b_sign->parts->next;
1659 b_sign->type = TYPE_APPLICATION;
1660 b_sign->subtype = mutt_str_dup("pkcs7-signature");
1661 b_sign->filename = buf_strdup(signedfile);
1662 b_sign->d_filename = mutt_str_dup("smime.p7s");
1663 b_sign->use_disp = true;
1664 b_sign->disposition = DISP_ATTACH;
1665 b_sign->encoding = ENC_BASE64;
1666 b_sign->unlink = true; /* ok to remove this file after sending. */
1667
1668cleanup:
1669 if (fp_sign)
1670 {
1671 mutt_file_fclose(&fp_sign);
1672 mutt_file_unlink(buf_string(filetosign));
1673 }
1674 if (fp_smime_out)
1675 {
1676 mutt_file_fclose(&fp_smime_out);
1677 mutt_file_unlink(buf_string(signedfile));
1678 }
1679 buf_pool_release(&filetosign);
1680 buf_pool_release(&signedfile);
1681 return rc;
1682}
1683
1701static pid_t smime_invoke_verify(FILE **fp_smime_in, FILE **fp_smime_out,
1702 FILE **fp_smime_err, int fp_smime_infd,
1703 int fp_smime_outfd, int fp_smime_errfd,
1704 const char *fname, const char *sig_fname, int opaque)
1705{
1706 const char *const c_smime_verify_opaque_command = cs_subset_string(NeoMutt->sub, "smime_verify_opaque_command");
1707 const char *const c_smime_verify_command = cs_subset_string(NeoMutt->sub, "smime_verify_command");
1708 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd, fp_smime_outfd,
1709 fp_smime_errfd, fname, sig_fname, NULL, NULL, NULL, NULL, NULL,
1710 (opaque ? c_smime_verify_opaque_command : c_smime_verify_command));
1711}
1712
1728static pid_t smime_invoke_decrypt(FILE **fp_smime_in, FILE **fp_smime_out,
1729 FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd,
1730 int fp_smime_errfd, const char *fname)
1731{
1732 const char *const c_smime_decrypt_command = cs_subset_string(NeoMutt->sub, "smime_decrypt_command");
1733 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd,
1734 fp_smime_outfd, fp_smime_errfd, fname, NULL, NULL, NULL,
1736 NULL, c_smime_decrypt_command);
1737}
1738
1742int smime_class_verify_one(struct Body *b, struct State *state, const char *tempfile)
1743{
1744 FILE *fp = NULL, *fp_smime_out = NULL, *fp_smime_err = NULL;
1745 pid_t pid;
1746 int badsig = -1;
1747
1748 LOFF_T tmpoffset = 0;
1749 size_t tmplength = 0;
1750 int orig_type = b->type;
1751
1752 struct Buffer *signedfile = buf_pool_get();
1753
1754 buf_printf(signedfile, "%s.sig", tempfile);
1755
1756 /* decode to a tempfile, saving the original destination */
1757 fp = state->fp_out;
1758 state->fp_out = mutt_file_fopen(buf_string(signedfile), "w");
1759 if (!state->fp_out)
1760 {
1761 mutt_perror("%s", buf_string(signedfile));
1762 goto cleanup;
1763 }
1764 /* decoding the attachment changes the size and offset, so save a copy
1765 * of the "real" values now, and restore them after processing */
1766 tmplength = b->length;
1767 tmpoffset = b->offset;
1768
1769 /* if we are decoding binary bodies, we don't want to prefix each
1770 * line with the prefix or else the data will get corrupted. */
1771 char *save_prefix = state->prefix;
1772 state->prefix = NULL;
1773
1774 mutt_decode_attachment(b, state);
1775
1776 b->length = ftello(state->fp_out);
1777 b->offset = 0;
1778 mutt_file_fclose(&state->fp_out);
1779
1780 /* restore final destination and substitute the tempfile for input */
1781 state->fp_out = fp;
1782 fp = state->fp_in;
1783 state->fp_in = fopen(buf_string(signedfile), "r");
1784
1785 /* restore the prefix */
1786 state->prefix = save_prefix;
1787
1788 b->type = orig_type;
1789
1790 fp_smime_err = mutt_file_mkstemp();
1791 if (!fp_smime_err)
1792 {
1793 mutt_perror(_("Can't create temporary file"));
1794 goto cleanup;
1795 }
1796
1797 crypt_current_time(state, "OpenSSL");
1798
1799 pid = smime_invoke_verify(NULL, &fp_smime_out, NULL, -1, -1, fileno(fp_smime_err),
1800 tempfile, buf_string(signedfile), 0);
1801 if (pid != -1)
1802 {
1803 fflush(fp_smime_out);
1804 mutt_file_fclose(&fp_smime_out);
1805
1806 if (filter_wait(pid))
1807 {
1808 badsig = -1;
1809 }
1810 else
1811 {
1812 char *line = NULL;
1813 size_t linelen;
1814
1815 fflush(fp_smime_err);
1816 rewind(fp_smime_err);
1817
1818 line = mutt_file_read_line(line, &linelen, fp_smime_err, NULL, MUTT_RL_NO_FLAGS);
1819 if (linelen && mutt_istr_equal(line, "verification successful"))
1820 badsig = 0;
1821
1822 FREE(&line);
1823 }
1824 }
1825
1826 fflush(fp_smime_err);
1827 rewind(fp_smime_err);
1828 mutt_file_copy_stream(fp_smime_err, state->fp_out);
1829 mutt_file_fclose(&fp_smime_err);
1830
1831 state_attach_puts(state, _("[-- End of OpenSSL output --]\n\n"));
1832
1833 mutt_file_unlink(buf_string(signedfile));
1834
1835 b->length = tmplength;
1836 b->offset = tmpoffset;
1837
1838 /* restore the original source stream */
1839 mutt_file_fclose(&state->fp_in);
1840 state->fp_in = fp;
1841
1842cleanup:
1843 buf_pool_release(&signedfile);
1844 return badsig;
1845}
1846
1856static struct Body *smime_handle_entity(struct Body *b, struct State *state, FILE *fp_out_file)
1857{
1858 struct Buffer tmpfname = buf_make(0);
1859 FILE *fp_smime_out = NULL, *fp_smime_in = NULL, *fp_smime_err = NULL;
1860 FILE *fp_tmp = NULL, *fp_out = NULL;
1861 struct Body *p = NULL;
1862 pid_t pid = -1;
1864
1865 if (!(type & APPLICATION_SMIME))
1866 return NULL;
1867
1868 /* Because of the mutt_body_handler() we avoid the buffer pool. */
1869 fp_smime_out = mutt_file_mkstemp();
1870 if (!fp_smime_out)
1871 {
1872 mutt_perror(_("Can't create temporary file"));
1873 goto cleanup;
1874 }
1875
1876 fp_smime_err = mutt_file_mkstemp();
1877 if (!fp_smime_err)
1878 {
1879 mutt_perror(_("Can't create temporary file"));
1880 goto cleanup;
1881 }
1882
1883 buf_mktemp(&tmpfname);
1884 fp_tmp = mutt_file_fopen(buf_string(&tmpfname), "w+");
1885 if (!fp_tmp)
1886 {
1887 mutt_perror("%s", buf_string(&tmpfname));
1888 goto cleanup;
1889 }
1890
1891 if (!mutt_file_seek(state->fp_in, b->offset, SEEK_SET))
1892 {
1893 goto cleanup;
1894 }
1895
1896 mutt_file_copy_bytes(state->fp_in, fp_tmp, b->length);
1897
1898 fflush(fp_tmp);
1899 mutt_file_fclose(&fp_tmp);
1900
1901 if ((type & SEC_ENCRYPT) &&
1902 ((pid = smime_invoke_decrypt(&fp_smime_in, NULL, NULL, -1, fileno(fp_smime_out),
1903 fileno(fp_smime_err), buf_string(&tmpfname))) == -1))
1904 {
1905 mutt_file_unlink(buf_string(&tmpfname));
1906 if (state->flags & STATE_DISPLAY)
1907 {
1908 state_attach_puts(state, _("[-- Error: unable to create OpenSSL subprocess --]\n"));
1909 }
1910 goto cleanup;
1911 }
1912 else if ((type & SEC_SIGNOPAQUE) &&
1913 ((pid = smime_invoke_verify(&fp_smime_in, NULL, NULL, -1,
1914 fileno(fp_smime_out), fileno(fp_smime_err), NULL,
1915 buf_string(&tmpfname), SEC_SIGNOPAQUE)) == -1))
1916 {
1917 mutt_file_unlink(buf_string(&tmpfname));
1918 if (state->flags & STATE_DISPLAY)
1919 {
1920 state_attach_puts(state, _("[-- Error: unable to create OpenSSL subprocess --]\n"));
1921 }
1922 goto cleanup;
1923 }
1924
1925 if (type & SEC_ENCRYPT)
1926 {
1929 fputs(SmimePass, fp_smime_in);
1930 fputc('\n', fp_smime_in);
1931 }
1932
1933 mutt_file_fclose(&fp_smime_in);
1934
1935 filter_wait(pid);
1936 mutt_file_unlink(buf_string(&tmpfname));
1937
1938 if (state->flags & STATE_DISPLAY)
1939 {
1940 fflush(fp_smime_err);
1941 rewind(fp_smime_err);
1942
1943 const int c = fgetc(fp_smime_err);
1944 if (c != EOF)
1945 {
1946 ungetc(c, fp_smime_err);
1947
1948 crypt_current_time(state, "OpenSSL");
1949 mutt_file_copy_stream(fp_smime_err, state->fp_out);
1950 state_attach_puts(state, _("[-- End of OpenSSL output --]\n\n"));
1951 }
1952
1953 if (type & SEC_ENCRYPT)
1954 {
1955 state_attach_puts(state, _("[-- The following data is S/MIME encrypted --]\n"));
1956 }
1957 else
1958 {
1959 state_attach_puts(state, _("[-- The following data is S/MIME signed --]\n"));
1960 }
1961 }
1962
1963 fflush(fp_smime_out);
1964 rewind(fp_smime_out);
1965
1966 if (type & SEC_ENCRYPT)
1967 {
1968 /* void the passphrase, even if that wasn't the problem */
1969 if (fgetc(fp_smime_out) == EOF)
1970 {
1971 mutt_error(_("Decryption failed"));
1973 }
1974 rewind(fp_smime_out);
1975 }
1976
1977 if (fp_out_file)
1978 {
1979 fp_out = fp_out_file;
1980 }
1981 else
1982 {
1983 fp_out = mutt_file_mkstemp();
1984 if (!fp_out)
1985 {
1986 mutt_perror(_("Can't create temporary file"));
1987 goto cleanup;
1988 }
1989 }
1990 char buf[8192] = { 0 };
1991 while (fgets(buf, sizeof(buf) - 1, fp_smime_out))
1992 {
1993 const size_t len = mutt_str_len(buf);
1994 if ((len > 1) && (buf[len - 2] == '\r'))
1995 {
1996 buf[len - 2] = '\n';
1997 buf[len - 1] = '\0';
1998 }
1999 fputs(buf, fp_out);
2000 }
2001 fflush(fp_out);
2002 rewind(fp_out);
2003
2004 const long size = mutt_file_get_size_fp(fp_out);
2005 if (size == 0)
2006 {
2007 goto cleanup;
2008 }
2009 p = mutt_read_mime_header(fp_out, 0);
2010 if (p)
2011 {
2012 p->length = size - p->offset;
2013
2014 mutt_parse_part(fp_out, p);
2015
2016 if (state->flags & STATE_DISPLAY)
2018
2019 /* Store any protected headers in the parent so they can be
2020 * accessed for index updates after the handler recursion is done.
2021 * This is done before the handler to prevent a nested encrypted
2022 * handler from freeing the headers. */
2024 b->mime_headers = p->mime_headers;
2025 p->mime_headers = NULL;
2026
2027 if (state->fp_out)
2028 {
2029 rewind(fp_out);
2030 FILE *fp_tmp_buffer = state->fp_in;
2031 state->fp_in = fp_out;
2032 mutt_body_handler(p, state);
2033 state->fp_in = fp_tmp_buffer;
2034 }
2035
2036 /* Embedded multipart signed protected headers override the
2037 * encrypted headers. We need to do this after the handler so
2038 * they can be printed in the pager. */
2039 if (!(type & SMIME_SIGN) && mutt_is_multipart_signed(p) && p->parts &&
2040 p->parts->mime_headers)
2041 {
2044 p->parts->mime_headers = NULL;
2045 }
2046 }
2047 mutt_file_fclose(&fp_smime_out);
2048
2049 if (!fp_out_file)
2050 {
2051 mutt_file_fclose(&fp_out);
2052 mutt_file_unlink(buf_string(&tmpfname));
2053 }
2054 fp_out = NULL;
2055
2056 if (state->flags & STATE_DISPLAY)
2057 {
2058 if (type & SEC_ENCRYPT)
2059 state_attach_puts(state, _("\n[-- End of S/MIME encrypted data. --]\n"));
2060 else
2061 state_attach_puts(state, _("\n[-- End of S/MIME signed data. --]\n"));
2062 }
2063
2064 if (type & SEC_SIGNOPAQUE)
2065 {
2066 char *line = NULL;
2067 size_t linelen;
2068
2069 rewind(fp_smime_err);
2070
2071 line = mutt_file_read_line(line, &linelen, fp_smime_err, NULL, MUTT_RL_NO_FLAGS);
2072 if (linelen && mutt_istr_equal(line, "verification successful"))
2073 b->goodsig = true;
2074 FREE(&line);
2075 }
2076 else if (p)
2077 {
2078 b->goodsig = p->goodsig;
2079 b->badsig = p->badsig;
2080 }
2081
2082cleanup:
2083 mutt_file_fclose(&fp_smime_out);
2084 mutt_file_fclose(&fp_smime_err);
2085 mutt_file_fclose(&fp_tmp);
2086 mutt_file_fclose(&fp_out);
2087 buf_dealloc(&tmpfname);
2088 return p;
2089}
2090
2094int smime_class_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **b_dec)
2095{
2096 struct State state = { 0 };
2097 LOFF_T tmpoffset = b->offset;
2098 size_t tmplength = b->length;
2099 int rc = -1;
2100
2102 return -1;
2103
2104 if (b->parts)
2105 return -1;
2106
2107 state.fp_in = fp_in;
2108 if (!mutt_file_seek(state.fp_in, b->offset, SEEK_SET))
2109 {
2110 return -1;
2111 }
2112
2113 FILE *fp_tmp = mutt_file_mkstemp();
2114 if (!fp_tmp)
2115 {
2116 mutt_perror(_("Can't create temporary file"));
2117 return -1;
2118 }
2119
2120 state.fp_out = fp_tmp;
2121 mutt_decode_attachment(b, &state);
2122 fflush(fp_tmp);
2123 b->length = ftello(state.fp_out);
2124 b->offset = 0;
2125 rewind(fp_tmp);
2126 state.fp_in = fp_tmp;
2127 state.fp_out = 0;
2128
2130 if (!*fp_out)
2131 {
2132 mutt_perror(_("Can't create temporary file"));
2133 goto bail;
2134 }
2135
2136 *b_dec = smime_handle_entity(b, &state, *fp_out);
2137 if (!*b_dec)
2138 goto bail;
2139
2140 (*b_dec)->goodsig = b->goodsig;
2141 (*b_dec)->badsig = b->badsig;
2142 rc = 0;
2143
2144bail:
2145 b->length = tmplength;
2146 b->offset = tmpoffset;
2147 mutt_file_fclose(&fp_tmp);
2148 if (*fp_out)
2149 rewind(*fp_out);
2150
2151 return rc;
2152}
2153
2157int smime_class_application_handler(struct Body *b, struct State *state)
2158{
2159 int rc = -1;
2160
2161 /* clear out any mime headers before the handler, so they can't be spoofed. */
2163
2164 struct Body *tattach = smime_handle_entity(b, state, NULL);
2165 if (tattach)
2166 {
2167 rc = 0;
2168 mutt_body_free(&tattach);
2169 }
2170 return rc;
2171}
2172
2177{
2178 struct SmimeKey *key = NULL;
2179 const char *prompt = NULL;
2180 const char *letters = NULL;
2181 const char *choices = NULL;
2182 int choice;
2183
2185 return e->security;
2186
2188
2189 /* Opportunistic encrypt is controlling encryption.
2190 * NOTE: "Signing" and "Clearing" only adjust the sign bit, so we have different
2191 * letter choices for those. */
2192 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt");
2193 if (c_crypt_opportunistic_encrypt && (e->security & SEC_OPPENCRYPT))
2194 {
2195 /* L10N: S/MIME options (opportunistic encryption is on) */
2196 prompt = _("S/MIME (s)ign, encrypt (w)ith, sign (a)s, (c)lear, or (o)ppenc mode off?");
2197 /* L10N: S/MIME options (opportunistic encryption is on) */
2198 letters = _("swaco");
2199 choices = "SwaCo";
2200 }
2201 else if (c_crypt_opportunistic_encrypt)
2202 {
2203 /* Opportunistic encryption option is set, but is toggled off
2204 * for this message. */
2205 /* L10N: S/MIME options (opportunistic encryption is off) */
2206 prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, (c)lear, or (o)ppenc mode?");
2207 /* L10N: S/MIME options (opportunistic encryption is off) */
2208 letters = _("eswabco");
2209 choices = "eswabcO";
2210 }
2211 else
2212 {
2213 /* Opportunistic encryption is unset */
2214 /* L10N: S/MIME options */
2215 prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear?");
2216 /* L10N: S/MIME options */
2217 letters = _("eswabc");
2218 choices = "eswabc";
2219 }
2220
2221 choice = mw_multi_choice(prompt, letters);
2222 if (choice > 0)
2223 {
2224 switch (choices[choice - 1])
2225 {
2226 case 'a': /* sign (a)s */
2227 key = smime_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN, false);
2228 if (key)
2229 {
2230 cs_subset_str_string_set(NeoMutt->sub, "smime_sign_as", key->hash, NULL);
2231 smime_key_free(&key);
2232
2233 e->security |= SEC_SIGN;
2234
2235 /* probably need a different passphrase */
2237 }
2238
2239 break;
2240
2241 case 'b': /* (b)oth */
2242 e->security |= (SEC_ENCRYPT | SEC_SIGN);
2243 break;
2244
2245 case 'c': /* (c)lear */
2246 e->security &= ~(SEC_ENCRYPT | SEC_SIGN);
2247 break;
2248
2249 case 'C':
2250 e->security &= ~SEC_SIGN;
2251 break;
2252
2253 case 'e': /* (e)ncrypt */
2254 e->security |= SEC_ENCRYPT;
2255 e->security &= ~SEC_SIGN;
2256 break;
2257
2258 case 'O': /* oppenc mode on */
2261 break;
2262
2263 case 'o': /* oppenc mode off */
2264 e->security &= ~SEC_OPPENCRYPT;
2265 break;
2266
2267 case 'S': /* (s)ign in oppenc mode */
2268 e->security |= SEC_SIGN;
2269 break;
2270
2271 case 's': /* (s)ign */
2272 e->security &= ~SEC_ENCRYPT;
2273 e->security |= SEC_SIGN;
2274 break;
2275
2276 case 'w': /* encrypt (w)ith */
2277 {
2278 e->security |= SEC_ENCRYPT;
2279 do
2280 {
2281 struct Buffer errmsg = buf_make(0);
2282 int rc = CSR_SUCCESS;
2283 switch (mw_multi_choice(_("Choose algorithm family: (1) DES, (2) RC2, (3) AES, or (c)lear?"),
2284 // L10N: Options for: Choose algorithm family: (1) DES, (2) RC2, (3) AES, or (c)lear?
2285 _("123c")))
2286 {
2287 case 1:
2288 switch (choice = mw_multi_choice(_("(1) DES, (2) Triple-DES?"),
2289 // L10N: Options for: (1) DES, (2) Triple-DES
2290 _("12")))
2291 {
2292 case 1:
2293 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2294 "des", &errmsg);
2295 break;
2296 case 2:
2297 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2298 "des3", &errmsg);
2299 break;
2300 }
2301 break;
2302
2303 case 2:
2304 switch (choice = mw_multi_choice(_("(1) RC2-40, (2) RC2-64, (3) RC2-128?"),
2305 // L10N: Options for: (1) RC2-40, (2) RC2-64, (3) RC2-128
2306 _("123")))
2307 {
2308 case 1:
2309 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2310 "rc2-40", &errmsg);
2311 break;
2312 case 2:
2313 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2314 "rc2-64", &errmsg);
2315 break;
2316 case 3:
2317 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2318 "rc2-128", &errmsg);
2319 break;
2320 }
2321 break;
2322
2323 case 3:
2324 switch (choice = mw_multi_choice(_("(1) AES128, (2) AES192, (3) AES256?"),
2325 // L10N: Options for: (1) AES128, (2) AES192, (3) AES256
2326 _("123")))
2327 {
2328 case 1:
2329 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2330 "aes128", &errmsg);
2331 break;
2332 case 2:
2333 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2334 "aes192", &errmsg);
2335 break;
2336 case 3:
2337 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2338 "aes256", &errmsg);
2339 break;
2340 }
2341 break;
2342
2343 case 4:
2344 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2345 NULL, &errmsg);
2346 /* (c)lear */
2348
2349 case -1: /* Ctrl-G or Enter */
2350 choice = 0;
2351 break;
2352 }
2353
2354 if ((CSR_RESULT(rc) != CSR_SUCCESS) && !buf_is_empty(&errmsg))
2355 mutt_error("%s", buf_string(&errmsg));
2356
2357 buf_dealloc(&errmsg);
2358 } while (choice == -1);
2359 break;
2360 }
2361 }
2362 }
2363
2364 return e->security;
2365}
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition: address.c:460
Email Address Handling.
Email Aliases.
void mutt_expand_aliases(struct AddressList *al)
Expand aliases in a List of Addresses.
Definition: alias.c:298
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition: alias.c:570
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:466
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:389
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:407
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:542
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:349
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
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
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:1034
SecurityFlags mutt_is_multipart_signed(struct Body *b)
Is a message signed?
Definition: crypt.c:397
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:598
void crypt_current_time(struct State *state, const char *app_name)
Print the current time.
Definition: crypt.c:69
void crypt_convert_to_7bit(struct Body *b)
Convert an email to 7bit encoding.
Definition: crypt.c:798
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:184
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:150
Edit a string.
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.
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1760
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1330
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:262
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:936
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:763
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:636
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:232
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1578
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:733
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:196
#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:218
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: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
char ** EnvList
Private copy of the environment variables.
Definition: globals.c:83
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:2157
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:2094
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:840
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:2176
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:1534
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:1371
void smime_class_getkeys(struct Envelope *env)
Get the S/MIME keys required to encrypt this email - Implements CryptModuleSpecs::smime_getkeys() -.
Definition: smime.c:800
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:1170
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:1245
bool smime_class_valid_passphrase(void)
Ensure we have a valid passphrase - Implements CryptModuleSpecs::valid_passphrase() -.
Definition: smime.c:161
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:1742
void smime_class_void_passphrase(void)
Forget the cached passphrase - Implements CryptModuleSpecs::void_passphrase() -.
Definition: smime.c:152
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:210
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:739
struct SmimeKey * dlg_smime(struct SmimeKey *keys, const char *query)
Get the user to select a key -.
Definition: dlg_smime.c:195
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:275
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition: question.c:60
int mutt_protected_headers_handler(struct Body *b_email, struct State *state)
Handler for protected headers - Implements handler_t -.
Definition: crypt.c:1106
#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:1624
void mutt_decode_attachment(struct Body *b, struct State *state)
Decode an email's attachment.
Definition: handler.c:1898
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:754
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: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 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: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
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1022
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:57
#define MUTT_COMP_UNBUFFERED
Ignore macro buffer.
Definition: mutt.h:58
#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:329
Some miscellaneous functions.
uint16_t SecurityFlags
Flags, e.g. SEC_ENCRYPT.
Definition: lib.h:77
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition: lib.h:87
uint16_t KeyFlags
Flags describing PGP/SMIME keys, e.g. KEYFLAG_CANSIGN.
Definition: lib.h:126
#define SEC_SIGNOPAQUE
Email has an opaque signature (encrypted)
Definition: lib.h:84
#define SMIME_SIGN
Definition: lib.h:104
#define KEYFLAG_CANENCRYPT
Key is suitable for encryption.
Definition: lib.h:129
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:92
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:79
#define WithCrypto
Definition: lib.h:117
#define SEC_SIGN
Email is signed.
Definition: lib.h:80
#define KEYFLAG_CANSIGN
Key is suitable for signing.
Definition: lib.h:128
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition: parameter.c:110
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
int mutt_write_mime_body(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition: body.c:301
Convenience wrapper for the send headers.
struct Address * mutt_default_from(struct ConfigSubset *sub)
Get a default 'from' Address.
Definition: send.c:1444
GUI display the mailboxes in a side panel.
static struct Buffer SmimeIntermediateToUse
Smime intermediate certificate to use.
Definition: smime.c:76
static struct SmimeKey * smime_get_key_by_hash(const char *hash, bool only_public_key)
Find a key by its hash.
Definition: smime.c:575
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:1356
static char SmimePass[256]
Cached Smime Passphrase.
Definition: smime.c:67
static time_t SmimeExpTime
Unix time when SmimePass expires.
Definition: smime.c:69
static void getkeys(const char *mailbox)
Get the keys for a mailbox.
Definition: smime.c:765
static struct SmimeKey * smime_copy_key(struct SmimeKey *key)
Copy an SMIME key.
Definition: smime.c:127
static void smime_command(char *buf, size_t buflen, struct SmimeCommandContext *cctx, const char *fmt)
Format an SMIME command string.
Definition: smime.c:382
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:682
void smime_init(void)
Initialise smime globals.
Definition: smime.c:81
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:1701
void smime_cleanup(void)
Clean up smime globals.
Definition: smime.c:91
static char * smime_extract_signer_certificate(const char *infile)
Extract the signer's certificate.
Definition: smime.c:1103
static struct Body * smime_handle_entity(struct Body *b, struct State *state, FILE *fp_out_file)
Handle type application/pkcs7-mime.
Definition: smime.c:1856
static struct SmimeKey * smime_parse_key(char *buf)
Parse an SMIME key block.
Definition: smime.c:444
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:1728
static struct SmimeKey * smime_get_candidates(const char *search, bool only_public_key)
Find keys matching a string.
Definition: smime.c:527
static struct Buffer SmimeKeyToUse
Smime key to use.
Definition: smime.c:72
static char * smime_extract_certificate(const char *infile)
Extract an SMIME certificate from a file.
Definition: smime.c:988
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:887
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:601
static struct Buffer SmimeCertToUse
Smime certificate to use.
Definition: smime.c:74
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:728
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:1329
static void smime_key_free(struct SmimeKey **keylist)
Free a list of SMIME keys.
Definition: smime.c:102
static char * openssl_md_to_smime_micalg(const char *md)
Change the algorithm names.
Definition: smime.c:1513
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:412
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: 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:58
const char * sig_fname
s
Definition: smime.h:63
const char * intermediates
i
Definition: smime.h:65
const char * digestalg
d
Definition: smime.h:61
const char * cryptalg
a
Definition: smime.h:60
const char * key
k
Definition: smime.h:59
const char * fname
f
Definition: smime.h:62
const char * certificates
c
Definition: smime.h:64
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:407
#define buf_mktemp(buf)
Definition: tmp.h:33
#define mutt_file_mkstemp()
Definition: tmp.h:36