2 * Copyright notice from original mutt:
3 * Copyright (C) 2001,2002 Oliver Ehli <elmy@acm.org>
4 * Copyright (C) 2002 Mike Schiraldi <raldi@research.netsol.com>
5 * Copyright (C) 2004 g10 Code GmbH
7 * This file is part of mutt-ng, see http://www.muttng.org/.
8 * It's licensed under the GNU General Public License,
9 * please see the file GPL in the top level source directory.
16 #include <lib-lib/mem.h>
17 #include <lib-lib/str.h>
18 #include <lib-lib/macros.h>
19 #include <lib-lib/file.h>
24 #include "mutt_curses.h"
25 #include "mutt_menu.h"
30 #include "lib/debug.h"
44 #ifdef HAVE_SYS_TIME_H
45 # include <sys/time.h>
48 #ifdef HAVE_SYS_RESOURCE_H
49 # include <sys/resource.h>
52 #ifdef CRYPT_BACKEND_CLASSIC_SMIME
54 #include "mutt_crypt.h"
56 struct smime_command_context {
57 const char *key; /* %k */
58 const char *cryptalg; /* %a */
59 const char *fname; /* %f */
60 const char *sig_fname; /* %s */
61 const char *certificates; /* %c */
62 const char *intermediates; /* %i */
71 char trust; /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
72 short public; /* 1=public 0=private */
76 char SmimePass[STRING];
77 time_t SmimeExptime = 0; /* when does the cached passphrase expire? */
80 static char SmimeKeyToUse[_POSIX_PATH_MAX] = { 0 };
81 static char SmimeCertToUse[_POSIX_PATH_MAX];
82 static char SmimeIntermediateToUse[_POSIX_PATH_MAX];
86 * Queries and passphrase handling.
92 /* these are copies from pgp.c */
95 void smime_void_passphrase (void)
97 p_clear(SmimePass, sizeof(SmimePass));
101 int smime_valid_passphrase (void)
103 time_t now = time (NULL);
105 if (now < SmimeExptime)
106 /* Use cached copy. */
109 smime_void_passphrase ();
111 if (mutt_get_field_unbuffered (_("Enter S/MIME passphrase:"), SmimePass,
112 sizeof (SmimePass), M_PASS) == 0) {
113 SmimeExptime = time (NULL) + SmimeTimeout;
124 * The OpenSSL interface
127 /* This is almost identical to ppgp's invoking interface. */
129 static const char *_mutt_fmt_smime_command (char *dest,
134 const char *ifstring,
135 const char *elsestring,
140 struct smime_command_context *cctx = (struct smime_command_context *) data;
141 int optional = (flags & M_FORMAT_OPTIONAL);
147 char path[_POSIX_PATH_MAX];
148 char buf1[LONG_STRING], buf2[LONG_STRING];
151 strfcpy (path, NONULL (SmimeCALocation), sizeof (path));
152 mutt_expand_path (path, sizeof (path));
153 mutt_quote_filename (buf1, sizeof (buf1), path);
155 if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
156 snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
158 snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
160 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
161 snprintf (dest, destlen, fmt, buf2);
163 else if (!SmimeCALocation)
169 { /* certificate (list) */
171 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
172 snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
174 else if (!cctx->certificates)
180 { /* intermediate certificates */
182 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
183 snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
185 else if (!cctx->intermediates)
191 { /* detached signature */
193 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
194 snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
196 else if (!cctx->sig_fname)
204 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
205 snprintf (dest, destlen, fmt, NONULL (cctx->key));
213 { /* algorithm for encryption */
215 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
216 snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
224 { /* file to process */
226 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
227 snprintf (dest, destlen, fmt, NONULL (cctx->fname));
229 else if (!cctx->fname)
240 mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
242 else if (flags & M_FORMAT_OPTIONAL)
243 mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
251 static void mutt_smime_command (char *d, size_t dlen,
252 struct smime_command_context *cctx,
255 mutt_FormatString (d, dlen, NONULL (fmt), _mutt_fmt_smime_command,
256 (unsigned long) cctx, 0);
257 debug_print (2, ("%s\n", d));
260 static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
261 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
262 int smimeerrfd, const char *fname,
263 const char *sig_fname, const char *cryptalg,
264 const char *key, const char *certificates,
265 const char *intermediates, const char *format)
267 struct smime_command_context cctx;
268 char cmd[HUGE_STRING];
272 if (!format || !*format)
276 cctx.sig_fname = sig_fname;
278 cctx.cryptalg = cryptalg;
279 cctx.certificates = certificates;
280 cctx.intermediates = intermediates;
282 mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
284 return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
285 smimeinfd, smimeoutfd, smimeerrfd);
294 * Key and certificate handling.
300 Search the certificate index for given mailbox.
301 return certificate file name.
304 static void smime_entry (char *s, size_t l, MUTTMENU * menu, int num)
306 smime_id *Table = (smime_id *) menu->data;
307 smime_id this = Table[num];
308 const char *truststate;
310 switch (this.trust) {
312 truststate = N_("Trusted ");
315 truststate = N_("Verified ");
318 truststate = N_("Unverified");
321 truststate = N_("Expired ");
324 truststate = N_("Revoked ");
327 truststate = N_("Invalid ");
330 truststate = N_("Unknown ");
333 snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
334 truststate, this.email, this.nick);
336 snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
337 this.email, this.nick);
344 char *smime_ask_for_key (char *prompt, char *mailbox, short public)
348 long cert_num; /* Will contain the number of certificates.
349 * To be able to get it, the .index file will be read twice... */
350 char index_file[_POSIX_PATH_MAX];
352 char buf[LONG_STRING];
353 char fields[5][STRING];
354 int numFields, hash_suffix, done, cur; /* The current entry */
357 char helpstr[HUGE_STRING * 3];
362 prompt = _("Enter keyID: ");
363 snprintf (index_file, sizeof (index_file), "%s/.index",
364 public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));
366 index = fopen (index_file, "r");
368 mutt_perror (index_file);
373 while (!feof (index)) {
374 if (fgets (buf, sizeof (buf), index))
381 if (mutt_get_field (prompt, qry, sizeof (qry), 0))
383 snprintf (title, sizeof (title),
384 _("S/MIME certificates matching \"%s\"."), qry);
387 index = fopen (index_file, "r");
389 mutt_perror (index_file);
394 Table = p_new(smime_id, cert_num);
395 while (!feof (index)) {
397 fscanf (index, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
398 fields[0], &hash, &hash_suffix, fields[2]);
400 fscanf (index, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
401 fields[3], fields[4]);
403 /* 0=email 1=name 2=nick 3=intermediate 4=trust */
407 /* Check if query matches this certificate */
408 if (!str_isstr (fields[0], qry) && !str_isstr (fields[2], qry))
411 Table[cur].hash = hash;
412 Table[cur].suffix = hash_suffix;
413 m_strcpy(Table[cur].email, sizeof(Table[cur].email), fields[0]);
414 m_strcpy(Table[cur].nick, sizeof(Table[cur].nick), fields[2]);
415 Table[cur].trust = *fields[4];
416 Table[cur].public = public;
422 /* Make Helpstring */
424 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT);
425 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
426 mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME,
427 OP_GENERIC_SELECT_ENTRY);
428 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
429 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
430 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
432 /* Create the menu */
433 menu = mutt_new_menu ();
435 menu->make_entry = smime_entry;
436 menu->menu = MENU_SMIME;
437 menu->help = helpstr;
440 /* sorting keys might be done later - TODO */
447 switch (mutt_menuLoop (menu)) {
448 case OP_GENERIC_SELECT_ENTRY:
460 fname = p_new(char, 13); /* Hash + '.' + Suffix + \0 */
461 sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
466 mutt_menuDestroy (&menu);
468 set_option (OPTNEEDREDRAW);
477 char *smime_get_field_from_db (char *mailbox, char *query, short public,
480 int addr_len, query_len, found = 0, ask = 0, choice = 0;
481 char cert_path[_POSIX_PATH_MAX];
482 char buf[LONG_STRING], prompt[STRING];
483 char fields[5][STRING];
487 char key_trust_level = 0;
490 if (!mailbox && !query)
493 addr_len = mailbox ? m_strlen(mailbox) : 0;
494 query_len = query ? m_strlen(query) : 0;
498 /* index-file format:
499 mailbox certfile label issuer_certfile trust_flags\n
501 certfile is a hash value generated by openssl.
502 Note that this was done according to the OpenSSL
503 specs on their CA-directory.
506 snprintf (cert_path, sizeof (cert_path), "%s/.index",
507 (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));
509 if (!stat (cert_path, &info)) {
510 if ((fp = safe_fopen (cert_path, "r")) == NULL) {
511 mutt_perror (cert_path);
515 while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
516 if (mailbox && !(str_ncasecmp (mailbox, buf, addr_len))) {
517 numFields = sscanf (buf,
518 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
519 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
520 MUTT_FORMAT (STRING) "\n",
521 fields[0], fields[1],
522 fields[2], fields[3], fields[4]);
525 if (mailbox && public &&
527 *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
531 if (public && *fields[4] == 'u')
532 snprintf (prompt, sizeof (prompt),
534 ("ID %s is unverified. Do you want to use it for %s ?"),
536 else if (public && *fields[4] == 'v')
537 snprintf (prompt, sizeof (prompt),
538 _("Use (untrusted!) ID %s for %s ?"),
541 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
545 if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
551 else if (choice == M_NO) {
555 else if (choice == M_YES) {
556 strfcpy (key, fields[1], sizeof (key));
563 key_trust_level = *fields[4];
564 strfcpy (key, fields[1], sizeof (key));
569 numFields = sscanf (buf,
570 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
571 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
572 MUTT_FORMAT (STRING) "\n",
573 fields[0], fields[1],
574 fields[2], fields[3], fields[4]);
576 /* query = label: return certificate. */
577 if (numFields >= 3 &&
578 !(str_ncasecmp (query, fields[2], query_len))) {
580 strfcpy (key, fields[1], sizeof (key));
582 /* query = certificate: return intermediate certificate. */
583 else if (numFields >= 4 &&
584 !(str_ncasecmp (query, fields[1], query_len))) {
586 strfcpy (key, fields[3], sizeof (key));
593 if (public && *fields[4] == 'u')
594 snprintf (prompt, sizeof (prompt),
595 _("ID %s is unverified. Do you want to use it for %s ?"),
597 else if (public && *fields[4] == 'v')
598 snprintf (prompt, sizeof (prompt),
599 _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
601 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
603 choice = mutt_yesorno (prompt, M_NO);
604 if (choice == -1 || choice == M_NO)
607 else if (key_trust_level && may_ask) {
608 if (key_trust_level == 'u') {
609 snprintf (prompt, sizeof (prompt),
610 _("ID %s is unverified. Do you want to use it for %s ?"),
612 choice = mutt_yesorno (prompt, M_NO);
616 else if (key_trust_level == 'v') {
618 ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
626 /* Note: m_strdup("") returns NULL. */
627 return m_strdup(key);
634 This sets the '*ToUse' variables for an upcoming decryption, where
635 the reuquired key is different from SmimeDefaultKey.
638 void _smime_getkeys (char *mailbox)
643 k = smime_get_field_from_db (mailbox, NULL, 0, 1);
646 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
647 k = smime_ask_for_key (buf, mailbox, 0);
651 /* the key used last time. */
652 if (*SmimeKeyToUse &&
653 !str_casecmp (k, SmimeKeyToUse + m_strlen(SmimeKeys) + 1)) {
658 smime_void_passphrase ();
660 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
661 NONULL (SmimeKeys), k);
663 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
664 NONULL (SmimeCertificates), k);
666 if (str_casecmp (k, SmimeDefaultKey))
667 smime_void_passphrase ();
673 if (*SmimeKeyToUse) {
674 if (!str_casecmp (SmimeDefaultKey,
675 SmimeKeyToUse + m_strlen(SmimeKeys) + 1))
678 smime_void_passphrase ();
681 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
682 NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
684 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
685 NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
688 void smime_getkeys (ENVELOPE * env)
693 if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
694 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
695 NONULL (SmimeKeys), SmimeDefaultKey);
697 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
698 NONULL (SmimeCertificates), SmimeDefaultKey);
703 for (t = env->to; !found && t; t = t->next)
704 if (mutt_addr_is_user (t)) {
706 _smime_getkeys (t->mailbox);
708 for (t = env->cc; !found && t; t = t->next)
709 if (mutt_addr_is_user (t)) {
711 _smime_getkeys (t->mailbox);
713 if (!found && (t = mutt_default_from ())) {
714 _smime_getkeys (t->mailbox);
715 rfc822_free_address (&t);
719 /* This routine attempts to find the keyids of the recipients of a message.
720 * It returns NULL if any of the keys can not be found.
723 char *smime_findKeys (ADDRESS * to, ADDRESS * cc, ADDRESS * bcc)
725 char *keyID, *keylist = NULL;
726 size_t keylist_size = 0;
727 size_t keylist_used = 0;
728 ADDRESS *tmp = NULL, *addr = NULL;
729 ADDRESS **last = &tmp;
733 const char *fqdn = mutt_fqdn (1);
735 for (i = 0; i < 3; i++) {
750 *last = rfc822_cpy_adr (p);
752 last = &((*last)->next);
756 rfc822_qualify (tmp, fqdn);
758 tmp = mutt_remove_duplicates (tmp);
760 for (p = tmp; p; p = p->next) {
761 char buf[LONG_STRING];
765 if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
766 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
767 keyID = smime_ask_for_key (buf, q->mailbox, 1);
770 mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
772 rfc822_free_address (&tmp);
773 rfc822_free_address (&addr);
777 keylist_size += m_strlen(keyID) + 2;
778 p_realloc(&keylist, keylist_size);
779 sprintf (keylist + keylist_used, "%s\n", keyID); /* __SPRINTF_CHECKED__ */
780 keylist_used = m_strlen(keylist);
782 rfc822_free_address (&addr);
785 rfc822_free_address (&tmp);
794 static int smime_handle_cert_email (char *certificate, char *mailbox,
795 int copy, char ***buffer, int *num)
797 FILE *fpout = NULL, *fperr = NULL;
798 char tmpfname[_POSIX_PATH_MAX];
800 int ret = -1, count = 0;
803 mutt_mktemp (tmpfname);
804 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
805 mutt_perror (tmpfname);
808 mutt_unlink (tmpfname);
810 mutt_mktemp (tmpfname);
811 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
813 mutt_perror (tmpfname);
816 mutt_unlink (tmpfname);
818 if ((thepid = smime_invoke (NULL, NULL, NULL,
819 -1, fileno (fpout), fileno (fperr),
820 certificate, NULL, NULL, NULL, NULL, NULL,
821 SmimeGetCertEmailCommand)) == -1) {
822 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
828 mutt_wait_filter (thepid);
836 while ((fgets (email, sizeof (email), fpout))) {
837 *(email + m_strlen(email) - 1) = '\0';
838 if (str_ncasecmp (email, mailbox, m_strlen(mailbox)) == 0)
841 ret = ret < 0 ? 0 : ret;
847 mutt_copy_stream (fperr, stdout);
848 mutt_any_key_to_continue (_
849 ("Error: unable to create OpenSSL subprocess!"));
857 if (copy && buffer && num) {
859 *buffer = p_new(char *, count);
863 while ((fgets (email, sizeof (email), fpout))) {
864 *(email + m_strlen(email) - 1) = '\0';
865 (*buffer)[count] = p_dupstr(email, m_strlen(email));
880 static char *smime_extract_certificate (char *infile)
882 FILE *fpout = NULL, *fperr = NULL;
883 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
884 char tmpfname[_POSIX_PATH_MAX];
889 mutt_mktemp (tmpfname);
890 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
891 mutt_perror (tmpfname);
894 mutt_unlink (tmpfname);
896 mutt_mktemp (pk7out);
897 if ((fpout = safe_fopen (pk7out, "w+")) == NULL) {
899 mutt_perror (pk7out);
903 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
904 extract the full set of certificates directly.
906 if ((thepid = smime_invoke (NULL, NULL, NULL,
907 -1, fileno (fpout), fileno (fperr),
908 infile, NULL, NULL, NULL, NULL, NULL,
909 SmimePk7outCommand)) == -1) {
910 mutt_any_key_to_continue (_
911 ("Error: unable to create OpenSSL subprocess!"));
914 mutt_unlink (pk7out);
918 mutt_wait_filter (thepid);
925 empty = (fgetc (fpout) == EOF);
927 mutt_perror (pk7out);
928 mutt_copy_stream (fperr, stdout);
931 mutt_unlink (pk7out);
938 mutt_mktemp (certfile);
939 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
941 mutt_unlink (pk7out);
942 mutt_perror (certfile);
946 /* Step 2: Extract the certificates from a PKCS#7 structure.
948 if ((thepid = smime_invoke (NULL, NULL, NULL,
949 -1, fileno (fpout), fileno (fperr),
950 pk7out, NULL, NULL, NULL, NULL, NULL,
951 SmimeGetCertCommand)) == -1) {
952 mutt_any_key_to_continue (_
953 ("Error: unable to create OpenSSL subprocess!"));
956 mutt_unlink (pk7out);
957 mutt_unlink (certfile);
961 mutt_wait_filter (thepid);
963 mutt_unlink (pk7out);
969 empty = (fgetc (fpout) == EOF);
971 mutt_copy_stream (fperr, stdout);
974 mutt_unlink (certfile);
981 return m_strdup(certfile);
984 static char *smime_extract_signer_certificate (char *infile)
986 FILE *fpout = NULL, *fperr = NULL;
987 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
988 char tmpfname[_POSIX_PATH_MAX];
993 mutt_mktemp (tmpfname);
994 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
995 mutt_perror (tmpfname);
998 mutt_unlink (tmpfname);
1001 mutt_mktemp (certfile);
1002 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
1004 mutt_perror (certfile);
1008 /* Extract signer's certificate
1010 if ((thepid = smime_invoke (NULL, NULL, NULL,
1011 -1, -1, fileno (fperr),
1012 infile, NULL, NULL, NULL, certfile, NULL,
1013 SmimeGetSignerCertCommand)) == -1) {
1014 mutt_any_key_to_continue (_
1015 ("Error: unable to create OpenSSL subprocess!"));
1018 mutt_unlink (pk7out);
1019 mutt_unlink (certfile);
1023 mutt_wait_filter (thepid);
1029 empty = (fgetc (fpout) == EOF);
1032 mutt_copy_stream (fperr, stdout);
1033 mutt_any_key_to_continue (NULL);
1036 mutt_unlink (certfile);
1043 return m_strdup(certfile);
1049 /* Add a certificate and update index file (externally). */
1051 void smime_invoke_import (char *infile, char *mailbox)
1053 char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1054 FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1057 mutt_mktemp (tmpfname);
1058 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1059 mutt_perror (tmpfname);
1062 mutt_unlink (tmpfname);
1064 mutt_mktemp (tmpfname);
1065 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
1067 mutt_perror (tmpfname);
1070 mutt_unlink (tmpfname);
1074 if (option (OPTASKCERTLABEL))
1075 mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1078 if ((certfile = smime_extract_certificate (infile))) {
1081 if ((thepid = smime_invoke (&smimein, NULL, NULL,
1082 -1, fileno (fpout), fileno (fperr),
1083 certfile, NULL, NULL, NULL, NULL, NULL,
1084 SmimeImportCertCommand)) == -1) {
1085 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1088 fputs (buf, smimein);
1089 fputc ('\n', smimein);
1092 mutt_wait_filter (thepid);
1094 mutt_unlink (certfile);
1095 p_delete(&certfile);
1103 mutt_copy_stream (fpout, stdout);
1104 mutt_copy_stream (fperr, stdout);
1113 int smime_verify_sender (HEADER * h)
1115 char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1119 mutt_mktemp (tempfname);
1120 if (!(fpout = safe_fopen (tempfname, "w"))) {
1121 mutt_perror (tempfname);
1125 if (h->security & ENCRYPT)
1126 mutt_copy_message (fpout, Context, h,
1127 M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1128 CH_MIME | CH_WEED | CH_NONEWLINE);
1130 mutt_copy_message (fpout, Context, h, 0, 0);
1136 h->env->from = mutt_expand_aliases (h->env->from);
1137 mbox = h->env->from->mailbox;
1139 else if (h->env->sender) {
1140 h->env->sender = mutt_expand_aliases (h->env->sender);
1141 mbox = h->env->sender->mailbox;
1145 if ((certfile = smime_extract_signer_certificate (tempfname))) {
1146 mutt_unlink (tempfname);
1147 if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1149 mutt_any_key_to_continue (NULL);
1153 mutt_unlink (certfile);
1154 p_delete(&certfile);
1157 mutt_any_key_to_continue (_("no certfile"));
1160 mutt_any_key_to_continue (_("no mbox"));
1162 mutt_unlink (tempfname);
1175 * Creating S/MIME - bodies.
1182 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1183 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1184 int smimeerrfd, const char *fname,
1187 return smime_invoke (smimein, smimeout, smimeerr,
1188 smimeinfd, smimeoutfd, smimeerrfd,
1189 fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1190 SmimeEncryptCommand);
1195 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1196 int smimeinfd, int smimeoutfd, int smimeerrfd,
1199 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1200 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1201 SmimeCertToUse, SmimeIntermediateToUse,
1208 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1210 char buf[LONG_STRING], certfile[LONG_STRING];
1211 char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1212 char smimeinfile[_POSIX_PATH_MAX];
1213 char *cert_start = certlist, *cert_end = certlist;
1214 FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1219 mutt_mktemp (tempfile);
1220 if ((fpout = safe_fopen (tempfile, "w+")) == NULL) {
1221 mutt_perror (tempfile);
1225 mutt_mktemp (smimeerrfile);
1226 if ((smimeerr = safe_fopen (smimeerrfile, "w+")) == NULL) {
1227 mutt_perror (smimeerrfile);
1229 mutt_unlink (tempfile);
1232 mutt_unlink (smimeerrfile);
1234 mutt_mktemp (smimeinfile);
1235 if ((fptmp = safe_fopen (smimeinfile, "w+")) == NULL) {
1236 mutt_perror (smimeinfile);
1237 mutt_unlink (tempfile);
1245 int off = m_strlen(certfile);
1247 while (*++cert_end && *cert_end != '\n');
1251 snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1252 NONULL (SmimeCertificates), cert_start);
1254 cert_start = cert_end;
1258 /* write a MIME entity */
1259 mutt_write_mime_header (a, fptmp);
1260 fputc ('\n', fptmp);
1261 mutt_write_mime_body (a, fptmp);
1265 smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1266 fileno (fpout), fileno (smimeerr),
1267 smimeinfile, certfile)) == -1) {
1269 mutt_unlink (smimeinfile);
1270 mutt_unlink (certfile);
1276 mutt_wait_filter (thepid);
1277 mutt_unlink (smimeinfile);
1278 mutt_unlink (certfile);
1282 empty = (fgetc (fpout) == EOF);
1287 while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1289 fputs (buf, stdout);
1293 /* pause if there is any error output from SMIME */
1295 mutt_any_key_to_continue (NULL);
1298 /* fatal error while trying to encrypt message */
1300 mutt_any_key_to_continue _("No output from OpenSSL..");
1302 mutt_unlink (tempfile);
1306 t = mutt_new_body ();
1307 t->type = TYPEAPPLICATION;
1308 t->subtype = m_strdup("x-pkcs7-mime");
1309 mutt_set_parameter ("name", "smime.p7m", &t->parameter);
1310 mutt_set_parameter ("smime-type", "enveloped-data", &t->parameter);
1311 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
1313 t->disposition = DISPATTACH;
1314 t->d_filename = m_strdup("smime.p7m");
1315 t->filename = m_strdup(tempfile);
1316 t->unlink = 1; /*delete after sending the message */
1326 BODY *smime_sign_message (BODY * a)
1329 char buffer[LONG_STRING];
1330 char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1331 FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1335 char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1337 if (!intermediates) {
1338 mutt_message (_("Warning: Intermediate certificate not found."));
1339 intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
1342 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
1344 mutt_mktemp (filetosign);
1345 if ((sfp = safe_fopen (filetosign, "w+")) == NULL) {
1346 mutt_perror (filetosign);
1350 mutt_mktemp (signedfile);
1351 if ((smimeout = safe_fopen (signedfile, "w+")) == NULL) {
1352 mutt_perror (signedfile);
1354 mutt_unlink (filetosign);
1358 mutt_write_mime_header (a, sfp);
1360 mutt_write_mime_body (a, sfp);
1365 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1366 NONULL (SmimeKeys), SmimeDefaultKey);
1368 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1369 NONULL (SmimeCertificates), SmimeDefaultKey);
1371 snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1372 NONULL (SmimeCertificates), intermediates);
1376 if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1377 -1, fileno (smimeout), -1,
1378 filetosign)) == -1) {
1379 mutt_perror (_("Can't open OpenSSL subprocess!"));
1382 mutt_unlink (signedfile);
1383 mutt_unlink (filetosign);
1386 fputs (SmimePass, smimein);
1387 fputc ('\n', smimein);
1391 mutt_wait_filter (thepid);
1393 /* check for errors from OpenSSL */
1397 while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1399 fputs (buffer, stdout);
1406 empty = (fgetc (smimeout) == EOF);
1409 mutt_unlink (filetosign);
1413 mutt_any_key_to_continue (NULL);
1416 mutt_any_key_to_continue _("No output from OpenSSL...");
1418 mutt_unlink (signedfile);
1419 return (NULL); /* fatal error while signing */
1422 t = mutt_new_body ();
1423 t->type = TYPEMULTIPART;
1424 t->subtype = m_strdup("signed");
1425 t->encoding = ENC7BIT;
1427 t->disposition = DISPINLINE;
1429 mutt_generate_boundary (&t->parameter);
1430 /* check if this can be extracted from private key somehow.... */
1431 mutt_set_parameter ("micalg", "sha1", &t->parameter);
1432 mutt_set_parameter ("protocol", "application/x-pkcs7-signature",
1438 t->parts->next = mutt_new_body ();
1440 t->type = TYPEAPPLICATION;
1441 t->subtype = m_strdup("x-pkcs7-signature");
1442 t->filename = m_strdup(signedfile);
1443 t->d_filename = m_strdup("smime.p7s");
1445 t->disposition = DISPATTACH;
1446 t->encoding = ENCBASE64;
1447 t->unlink = 1; /* ok to remove this file after sending. */
1459 * Handling S/MIME - bodies.
1468 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1469 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1470 int smimeerrfd, const char *fname,
1471 const char *sig_fname, int opaque)
1473 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1474 smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1475 (opaque ? SmimeVerifyOpaqueCommand :
1476 SmimeVerifyCommand));
1481 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1482 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1483 int smimeerrfd, const char *fname)
1485 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1486 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1487 SmimeCertToUse, NULL, SmimeDecryptCommand);
1492 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1494 char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1495 FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1500 size_t tmplength = 0;
1501 int origType = sigbdy->type;
1502 char *savePrefix = NULL;
1505 snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1507 /* decode to a tempfile, saving the original destination */
1509 if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1510 mutt_perror (signedfile);
1513 /* decoding the attachment changes the size and offset, so save a copy
1514 * of the "real" values now, and restore them after processing
1516 tmplength = sigbdy->length;
1517 tmpoffset = sigbdy->offset;
1519 /* if we are decoding binary bodies, we don't want to prefix each
1520 * line with the prefix or else the data will get corrupted.
1522 savePrefix = s->prefix;
1525 mutt_decode_attachment (sigbdy, s);
1527 sigbdy->length = ftello (s->fpout);
1531 /* restore final destination and substitute the tempfile for input */
1534 s->fpin = fopen (signedfile, "r");
1536 /* restore the prefix */
1537 s->prefix = savePrefix;
1539 sigbdy->type = origType;
1542 mutt_mktemp (smimeerrfile);
1543 if (!(smimeerr = safe_fopen (smimeerrfile, "w+"))) {
1544 mutt_perror (smimeerrfile);
1545 mutt_unlink (signedfile);
1549 crypt_current_time (s, "OpenSSL");
1551 if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1552 -1, -1, fileno (smimeerr),
1553 tempfile, signedfile, 0)) != -1) {
1557 if (mutt_wait_filter (thepid))
1567 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1568 if (linelen && !str_casecmp (line, "verification successful"))
1577 mutt_copy_stream (smimeerr, s->fpout);
1580 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1582 mutt_unlink (signedfile);
1583 mutt_unlink (smimeerrfile);
1585 sigbdy->length = tmplength;
1586 sigbdy->offset = tmpoffset;
1588 /* restore the original source stream */
1601 This handles application/pkcs7-mime which can either be a signed
1602 or an encrypted message.
1605 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1610 char buf[HUGE_STRING];
1611 char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1612 char tmpfname[_POSIX_PATH_MAX];
1613 char tmptmpfname[_POSIX_PATH_MAX];
1614 FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1615 FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1619 unsigned int type = mutt_is_application_smime (m);
1621 if (!(type & APPLICATION_SMIME))
1624 mutt_mktemp (outfile);
1625 if ((smimeout = safe_fopen (outfile, "w+")) == NULL) {
1626 mutt_perror (outfile);
1630 mutt_mktemp (errfile);
1631 if ((smimeerr = safe_fopen (errfile, "w+")) == NULL) {
1632 mutt_perror (errfile);
1637 mutt_unlink (errfile);
1640 mutt_mktemp (tmpfname);
1641 if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL) {
1642 mutt_perror (tmpfname);
1650 fseeko (s->fpin, m->offset, 0);
1651 last_pos = m->offset;
1653 mutt_copy_bytes (s->fpin, tmpfp, m->length);
1658 if ((type & ENCRYPT) &&
1659 (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1660 fileno (smimeout), fileno (smimeerr),
1664 mutt_unlink (tmpfname);
1665 if (s->flags & M_DISPLAY)
1666 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1669 else if ((type & SIGNOPAQUE) &&
1670 (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1672 fileno (smimeerr), NULL, tmpfname,
1673 SIGNOPAQUE)) == -1) {
1676 mutt_unlink (tmpfname);
1677 if (s->flags & M_DISPLAY)
1678 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1683 if (type & ENCRYPT) {
1684 if (!smime_valid_passphrase ())
1685 smime_void_passphrase ();
1686 fputs (SmimePass, smimein);
1687 fputc ('\n', smimein);
1692 mutt_wait_filter (thepid);
1693 mutt_unlink (tmpfname);
1696 if (s->flags & M_DISPLAY) {
1699 if ((c = fgetc (smimeerr)) != EOF) {
1700 ungetc (c, smimeerr);
1702 crypt_current_time (s, "OpenSSL");
1703 mutt_copy_stream (smimeerr, s->fpout);
1704 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1708 state_attach_puts (_("[-- The following data is S/MIME"
1709 " encrypted --]\n"), s);
1711 state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1722 mutt_mktemp (tmptmpfname);
1723 if ((fpout = safe_fopen (tmptmpfname, "w+")) == NULL) {
1724 mutt_perror (tmptmpfname);
1730 while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1731 len = m_strlen(buf);
1732 if (len > 1 && buf[len - 2] == '\r') {
1733 buf[len - 2] = '\n';
1734 buf[len - 1] = '\0';
1742 if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1743 fstat (fileno (fpout), &info);
1744 p->length = info.st_size - p->offset;
1746 mutt_parse_part (fpout, p);
1749 tmpfp_buffer = s->fpin;
1751 mutt_body_handler (p, s);
1752 s->fpin = tmpfp_buffer;
1758 mutt_unlink (outfile);
1762 mutt_unlink (tmptmpfname);
1767 if (s->flags & M_DISPLAY) {
1769 state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1771 state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1774 if (type & SIGNOPAQUE) {
1781 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1782 if (linelen && !str_casecmp (line, "verification successful"))
1787 m->goodsig = p->goodsig;
1788 m->badsig = p->badsig;
1799 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1803 char tempfile[_POSIX_PATH_MAX];
1805 long tmpoffset = b->offset;
1806 size_t tmplength = b->length;
1807 int origType = b->type;
1811 if (!mutt_is_application_smime (b))
1819 fseeko (s.fpin, b->offset, 0);
1821 mutt_mktemp (tempfile);
1822 if ((tmpfp = safe_fopen (tempfile, "w+")) == NULL) {
1823 mutt_perror (tempfile);
1827 mutt_unlink (tempfile);
1829 mutt_decode_attachment (b, &s);
1831 b->length = ftello (s.fpout);
1837 mutt_mktemp (tempfile);
1838 if ((*fpout = safe_fopen (tempfile, "w+")) == NULL) {
1839 mutt_perror (tempfile);
1843 mutt_unlink (tempfile);
1845 if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1850 (*cur)->goodsig = b->goodsig;
1851 (*cur)->badsig = b->badsig;
1855 b->length = tmplength;
1856 b->offset = tmpoffset;
1858 safe_fclose (&tmpfp);
1865 int smime_application_smime_handler (BODY * m, STATE * s)
1867 return smime_handle_entity (m, s, NULL) ? 0 : -1;
1870 int smime_send_menu (HEADER * msg, int *redraw)
1874 if (!(WithCrypto & APPLICATION_SMIME))
1875 return msg->security;
1877 switch (mutt_multi_choice
1879 ("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1881 case 1: /* (e)ncrypt */
1882 msg->security |= ENCRYPT;
1883 msg->security &= ~SIGN;
1886 case 3: /* encrypt (w)ith */
1889 msg->security |= ENCRYPT;
1892 /* I use "dra" because "123" is recognized anyway */
1893 switch (mutt_multi_choice (_("Choose algorithm family:"
1894 " 1: DES, 2: RC2, 3: AES,"
1895 " or (c)lear? "), _("drac"))) {
1897 switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
1900 str_replace (&SmimeCryptAlg, "des");
1903 str_replace (&SmimeCryptAlg, "des3");
1909 switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
1912 str_replace (&SmimeCryptAlg, "rc2-40");
1915 str_replace (&SmimeCryptAlg, "rc2-64");
1918 str_replace (&SmimeCryptAlg, "rc2-128");
1924 switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
1927 str_replace (&SmimeCryptAlg, "aes128");
1930 str_replace (&SmimeCryptAlg, "aes192");
1933 str_replace (&SmimeCryptAlg, "aes256");
1938 case 4: /* (c)lear */
1939 p_delete(&SmimeCryptAlg);
1941 case -1: /* Ctrl-G or Enter */
1945 } while (choice == -1);
1949 case 2: /* (s)ign */
1951 if (!SmimeDefaultKey)
1952 mutt_message (_("Can't sign: No key specified. Use Sign As."));
1955 msg->security |= SIGN;
1956 msg->security &= ~ENCRYPT;
1960 case 4: /* sign (a)s */
1962 if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1963 str_replace (&SmimeDefaultKey, p);
1965 msg->security |= SIGN;
1967 /* probably need a different passphrase */
1968 crypt_smime_void_passphrase ();
1972 msg->security &= ~SIGN;
1975 *redraw = REDRAW_FULL;
1978 case 5: /* (b)oth */
1979 msg->security |= (ENCRYPT | SIGN);
1982 case 6: /* (f)orget it */
1983 case 7: /* (c)lear */
1988 if (msg->security && msg->security != APPLICATION_SMIME)
1989 msg->security |= APPLICATION_SMIME;
1993 return (msg->security);
1997 #endif /* CRYPT_BACKEND_CLASSIC_SMIME */