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.
27 #ifdef HAVE_SYS_TIME_H
28 # include <sys/time.h>
30 #ifdef HAVE_SYS_RESOURCE_H
31 # include <sys/resource.h>
34 #include <lib-lib/lib-lib.h>
36 #include <lib-mime/mime.h>
38 #include <lib-ui/curses.h>
39 #include <lib-ui/enter.h>
40 #include <lib-ui/menu.h>
49 struct smime_command_context {
50 const char *key; /* %k */
51 const char *cryptalg; /* %a */
52 const char *fname; /* %f */
53 const char *sig_fname; /* %s */
54 const char *certificates; /* %c */
55 const char *intermediates; /* %i */
64 char trust; /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
65 short public; /* 1=public 0=private */
69 char SmimePass[STRING];
70 time_t SmimeExptime = 0; /* when does the cached passphrase expire? */
73 static char SmimeKeyToUse[_POSIX_PATH_MAX] = { 0 };
74 static char SmimeCertToUse[_POSIX_PATH_MAX];
75 static char SmimeIntermediateToUse[_POSIX_PATH_MAX];
79 * Create a format string to be used with scanf.
80 * To use it, write, for instance, MUTT_FORMAT(HUGE_STRING).
82 * See K&R 2nd ed, p. 231 for an explanation.
84 #define _MUTT_FORMAT_2(a,b) "%" a b
85 #define _MUTT_FORMAT_1(a, b) _MUTT_FORMAT_2(#a, b)
86 #define MUTT_FORMAT(a) _MUTT_FORMAT_1(a, "s")
90 * Queries and passphrase handling.
94 /* these are copies from pgp.c */
97 void smime_void_passphrase (void)
99 p_clear(SmimePass, countof(SmimePass));
103 int smime_valid_passphrase (void)
105 time_t now = time (NULL);
107 if (now < SmimeExptime)
108 /* Use cached copy. */
111 smime_void_passphrase ();
113 if (mutt_get_field_unbuffered (_("Enter S/MIME passphrase:"), SmimePass,
114 sizeof (SmimePass), M_PASS) == 0) {
115 SmimeExptime = time (NULL) + SmimeTimeout;
126 * The OpenSSL interface
129 /* This is almost identical to ppgp's invoking interface. */
132 _mutt_fmt_smime_command (char *dest, ssize_t destlen, char op,
133 const char *src, const char *prefix,
134 const char *ifstring, const char *elsestring,
135 unsigned long data, format_flag flags)
138 struct smime_command_context *cctx = (struct smime_command_context *) data;
139 int optional = (flags & M_FORMAT_OPTIONAL);
145 char path[_POSIX_PATH_MAX];
146 char buf1[LONG_STRING], buf2[LONG_STRING];
149 m_strcpy(path, sizeof(path), NONULL(SmimeCALocation));
150 mutt_expand_path (path, sizeof (path));
151 mutt_quote_filename (buf1, sizeof (buf1), path);
153 if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
154 snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
156 snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
158 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
159 snprintf (dest, destlen, fmt, buf2);
161 else if (!SmimeCALocation)
167 { /* certificate (list) */
169 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
170 snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
172 else if (!cctx->certificates)
178 { /* intermediate certificates */
180 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
181 snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
183 else if (!cctx->intermediates)
189 { /* detached signature */
191 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
192 snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
194 else if (!cctx->sig_fname)
202 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
203 snprintf (dest, destlen, fmt, NONULL (cctx->key));
211 { /* algorithm for encryption */
213 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
214 snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
222 { /* file to process */
224 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
225 snprintf (dest, destlen, fmt, NONULL (cctx->fname));
227 else if (!cctx->fname)
238 mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
240 else if (flags & M_FORMAT_OPTIONAL)
241 mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
249 static void mutt_smime_command (char *d, ssize_t dlen,
250 struct smime_command_context *cctx,
253 mutt_FormatString (d, dlen, NONULL (fmt), _mutt_fmt_smime_command,
254 (unsigned long) cctx, 0);
257 static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
258 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
259 int smimeerrfd, const char *fname,
260 const char *sig_fname, const char *cryptalg,
261 const char *key, const char *certificates,
262 const char *intermediates, const char *format)
264 struct smime_command_context cctx;
265 char cmd[HUGE_STRING];
269 if (!format || !*format)
273 cctx.sig_fname = sig_fname;
275 cctx.cryptalg = cryptalg;
276 cctx.certificates = certificates;
277 cctx.intermediates = intermediates;
279 mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
281 return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
282 smimeinfd, smimeoutfd, smimeerrfd);
291 * Key and certificate handling.
297 Search the certificate index for given mailbox.
298 return certificate file name.
301 static void smime_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
303 smime_id *Table = (smime_id *) menu->data;
304 smime_id this = Table[num];
305 const char *truststate;
307 switch (this.trust) {
309 truststate = N_("Trusted ");
312 truststate = N_("Verified ");
315 truststate = N_("Unverified");
318 truststate = N_("Expired ");
321 truststate = N_("Revoked ");
324 truststate = N_("Invalid ");
327 truststate = N_("Unknown ");
330 snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
331 truststate, this.email, this.nick);
333 snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
334 this.email, this.nick);
341 char *smime_ask_for_key (char *prompt, char *mailbox __attribute__((unused)),
346 long cert_num; /* Will contain the number of certificates.
347 * To be able to get it, the .index file will be read twice... */
348 char index_file[_POSIX_PATH_MAX];
350 char buf[LONG_STRING];
351 char fields[5][STRING];
352 int numFields, hash_suffix, done, cur; /* The current entry */
355 char helpstr[HUGE_STRING * 3];
360 prompt = _("Enter keyID: ");
361 snprintf (index_file, sizeof (index_file), "%s/.index",
362 public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));
364 idx = fopen (index_file, "r");
366 mutt_perror (index_file);
371 while (!feof (idx)) {
372 if (fgets (buf, sizeof (buf), idx))
379 if (mutt_get_field (prompt, qry, sizeof (qry), 0))
381 snprintf (title, sizeof (title),
382 _("S/MIME certificates matching \"%s\"."), qry);
385 idx = fopen (index_file, "r");
387 mutt_perror (index_file);
392 Table = p_new(smime_id, cert_num);
393 while (!feof (idx)) {
395 fscanf (idx, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
396 fields[0], &hash, &hash_suffix, fields[2]);
398 fscanf (idx, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
399 fields[3], fields[4]);
401 /* 0=email 1=name 2=nick 3=intermediate 4=trust */
405 /* Check if query matches this certificate */
406 if (!m_stristr(fields[0], qry) && !m_stristr(fields[2], qry))
409 Table[cur].hash = hash;
410 Table[cur].suffix = hash_suffix;
411 m_strcpy(Table[cur].email, sizeof(Table[cur].email), fields[0]);
412 m_strcpy(Table[cur].nick, sizeof(Table[cur].nick), fields[2]);
413 Table[cur].trust = *fields[4];
414 Table[cur].public = public;
420 /* Make Helpstring */
422 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT);
423 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
424 mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME,
425 OP_GENERIC_SELECT_ENTRY);
426 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
427 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
428 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
430 /* Create the menu */
431 menu = mutt_new_menu ();
433 menu->make_entry = smime_entry;
434 menu->menu = MENU_SMIME;
435 menu->help = helpstr;
438 /* sorting keys might be done later - TODO */
445 switch (mutt_menuLoop (menu)) {
446 case OP_GENERIC_SELECT_ENTRY:
458 fname = p_new(char, 13); /* Hash + '.' + Suffix + \0 */
459 sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
464 mutt_menuDestroy (&menu);
466 set_option (OPTNEEDREDRAW);
475 char *smime_get_field_from_db (char *mailbox, char *query, short public,
478 int addr_len, query_len, found = 0, ask = 0, choice = 0;
479 char cert_path[_POSIX_PATH_MAX];
480 char buf[LONG_STRING], prompt[STRING];
481 char fields[5][STRING];
485 char key_trust_level = 0;
488 if (!mailbox && !query)
491 addr_len = mailbox ? m_strlen(mailbox) : 0;
492 query_len = query ? m_strlen(query) : 0;
496 /* index-file format:
497 mailbox certfile label issuer_certfile trust_flags\n
499 certfile is a hash value generated by openssl.
500 Note that this was done according to the OpenSSL
501 specs on their CA-directory.
504 snprintf (cert_path, sizeof (cert_path), "%s/.index",
505 (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));
507 if (!stat (cert_path, &info)) {
508 if ((fp = safe_fopen (cert_path, "r")) == NULL) {
509 mutt_perror (cert_path);
513 while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
514 if (mailbox && !(m_strncasecmp(mailbox, buf, addr_len))) {
515 numFields = sscanf (buf,
516 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
517 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
518 MUTT_FORMAT (STRING) "\n",
519 fields[0], fields[1],
520 fields[2], fields[3], fields[4]);
523 if (mailbox && public &&
525 *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
529 if (public && *fields[4] == 'u')
530 snprintf (prompt, sizeof (prompt),
532 ("ID %s is unverified. Do you want to use it for %s ?"),
534 else if (public && *fields[4] == 'v')
535 snprintf (prompt, sizeof (prompt),
536 _("Use (untrusted!) ID %s for %s ?"),
539 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
543 if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
549 else if (choice == M_NO) {
553 else if (choice == M_YES) {
554 m_strcpy(key, sizeof(key), fields[1]);
561 key_trust_level = *fields[4];
562 m_strcpy(key, sizeof(key), fields[1]);
567 numFields = sscanf (buf,
568 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
569 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
570 MUTT_FORMAT (STRING) "\n",
571 fields[0], fields[1],
572 fields[2], fields[3], fields[4]);
574 /* query = label: return certificate. */
575 if (numFields >= 3 &&
576 !(m_strncasecmp(query, fields[2], query_len))) {
578 m_strcpy(key, sizeof(key), fields[1]);
580 /* query = certificate: return intermediate certificate. */
581 else if (numFields >= 4 &&
582 !(m_strncasecmp(query, fields[1], query_len))) {
584 m_strcpy(key, sizeof(key), fields[3]);
591 if (public && *fields[4] == 'u')
592 snprintf (prompt, sizeof (prompt),
593 _("ID %s is unverified. Do you want to use it for %s ?"),
595 else if (public && *fields[4] == 'v')
596 snprintf (prompt, sizeof (prompt),
597 _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
599 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
601 choice = mutt_yesorno (prompt, M_NO);
602 if (choice == -1 || choice == M_NO)
605 else if (key_trust_level && may_ask) {
606 if (key_trust_level == 'u') {
607 snprintf (prompt, sizeof (prompt),
608 _("ID %s is unverified. Do you want to use it for %s ?"),
610 choice = mutt_yesorno (prompt, M_NO);
614 else if (key_trust_level == 'v') {
616 ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
624 /* Note: m_strdup("") returns NULL. */
625 return m_strdup(key);
632 This sets the '*ToUse' variables for an upcoming decryption, where
633 the reuquired key is different from SmimeDefaultKey.
636 void _smime_getkeys (char *mailbox)
641 k = smime_get_field_from_db (mailbox, NULL, 0, 1);
644 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
645 k = smime_ask_for_key (buf, mailbox, 0);
649 /* the key used last time. */
650 if (*SmimeKeyToUse &&
651 !m_strcasecmp(k, SmimeKeyToUse + m_strlen(SmimeKeys) + 1)) {
656 smime_void_passphrase ();
658 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
659 NONULL (SmimeKeys), k);
661 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
662 NONULL (SmimeCertificates), k);
664 if (m_strcasecmp(k, SmimeDefaultKey))
665 smime_void_passphrase ();
671 if (*SmimeKeyToUse) {
672 if (!m_strcasecmp(SmimeDefaultKey,
673 SmimeKeyToUse + m_strlen(SmimeKeys) + 1))
676 smime_void_passphrase ();
679 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
680 NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
682 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
683 NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
686 void smime_getkeys (ENVELOPE * env)
691 if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
692 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
693 NONULL (SmimeKeys), SmimeDefaultKey);
695 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
696 NONULL (SmimeCertificates), SmimeDefaultKey);
701 for (t = env->to; !found && t; t = t->next)
702 if (mutt_addr_is_user (t)) {
704 _smime_getkeys (t->mailbox);
706 for (t = env->cc; !found && t; t = t->next)
707 if (mutt_addr_is_user (t)) {
709 _smime_getkeys (t->mailbox);
711 if (!found && (t = mutt_default_from ())) {
712 _smime_getkeys (t->mailbox);
713 address_list_wipe(&t);
717 /* This routine attempts to find the keyids of the recipients of a message.
718 * It returns NULL if any of the keys can not be found.
721 char *smime_findKeys (address_t * to, address_t * cc, address_t * bcc)
723 char *keyID, *keylist = NULL;
724 ssize_t keylist_size = 0;
725 ssize_t keylist_used = 0;
726 address_t *tmp = NULL, *addr = NULL;
727 address_t **last = &tmp;
731 const char *fqdn = mutt_fqdn (1);
733 for (i = 0; i < 3; i++) {
748 *last = address_list_dup (p);
750 last = &((*last)->next);
754 rfc822_qualify (tmp, fqdn);
756 address_list_uniq(&tmp);
758 for (p = tmp; p; p = p->next) {
759 char buf[LONG_STRING];
763 if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
764 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
765 keyID = smime_ask_for_key (buf, q->mailbox, 1);
768 mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
770 address_list_wipe(&tmp);
771 address_list_wipe(&addr);
775 keylist_size += m_strlen(keyID) + 2;
776 p_realloc(&keylist, keylist_size);
777 sprintf (keylist + keylist_used, "%s\n", keyID); /* __SPRINTF_CHECKED__ */
778 keylist_used = m_strlen(keylist);
780 address_list_wipe(&addr);
783 address_list_wipe(&tmp);
792 static int smime_handle_cert_email (char *certificate, char *mailbox,
793 int copy, char ***buffer, int *num)
795 FILE *fpout = NULL, *fperr = NULL;
796 char tmpfname[_POSIX_PATH_MAX];
798 int ret = -1, count = 0;
801 mutt_mktemp (tmpfname);
802 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
803 mutt_perror (tmpfname);
806 mutt_unlink (tmpfname);
808 mutt_mktemp (tmpfname);
809 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
811 mutt_perror (tmpfname);
814 mutt_unlink (tmpfname);
816 if ((thepid = smime_invoke (NULL, NULL, NULL,
817 -1, fileno (fpout), fileno (fperr),
818 certificate, NULL, NULL, NULL, NULL, NULL,
819 SmimeGetCertEmailCommand)) == -1) {
820 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
826 mutt_wait_filter (thepid);
834 while ((fgets (email, sizeof (email), fpout))) {
835 *(email + m_strlen(email) - 1) = '\0';
836 if (m_strncasecmp(email, mailbox, m_strlen(mailbox)) == 0)
839 ret = ret < 0 ? 0 : ret;
845 mutt_copy_stream (fperr, stdout);
846 mutt_any_key_to_continue (_
847 ("Error: unable to create OpenSSL subprocess!"));
855 if (copy && buffer && num) {
857 *buffer = p_new(char *, count);
861 while ((fgets (email, sizeof (email), fpout))) {
862 *(email + m_strlen(email) - 1) = '\0';
863 (*buffer)[count] = p_dupstr(email, m_strlen(email));
878 static char *smime_extract_certificate (char *infile)
880 FILE *fpout = NULL, *fperr = NULL;
881 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
882 char tmpfname[_POSIX_PATH_MAX];
887 mutt_mktemp (tmpfname);
888 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
889 mutt_perror (tmpfname);
892 mutt_unlink (tmpfname);
894 mutt_mktemp (pk7out);
895 if ((fpout = safe_fopen (pk7out, "w+")) == NULL) {
897 mutt_perror (pk7out);
901 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
902 extract the full set of certificates directly.
904 if ((thepid = smime_invoke (NULL, NULL, NULL,
905 -1, fileno (fpout), fileno (fperr),
906 infile, NULL, NULL, NULL, NULL, NULL,
907 SmimePk7outCommand)) == -1) {
908 mutt_any_key_to_continue (_
909 ("Error: unable to create OpenSSL subprocess!"));
912 mutt_unlink (pk7out);
916 mutt_wait_filter (thepid);
923 empty = (fgetc (fpout) == EOF);
925 mutt_perror (pk7out);
926 mutt_copy_stream (fperr, stdout);
929 mutt_unlink (pk7out);
936 mutt_mktemp (certfile);
937 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
939 mutt_unlink (pk7out);
940 mutt_perror (certfile);
944 /* Step 2: Extract the certificates from a PKCS#7 structure.
946 if ((thepid = smime_invoke (NULL, NULL, NULL,
947 -1, fileno (fpout), fileno (fperr),
948 pk7out, NULL, NULL, NULL, NULL, NULL,
949 SmimeGetCertCommand)) == -1) {
950 mutt_any_key_to_continue (_
951 ("Error: unable to create OpenSSL subprocess!"));
954 mutt_unlink (pk7out);
955 mutt_unlink (certfile);
959 mutt_wait_filter (thepid);
961 mutt_unlink (pk7out);
967 empty = (fgetc (fpout) == EOF);
969 mutt_copy_stream (fperr, stdout);
972 mutt_unlink (certfile);
979 return m_strdup(certfile);
982 static char *smime_extract_signer_certificate (char *infile)
984 FILE *fpout = NULL, *fperr = NULL;
985 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
986 char tmpfname[_POSIX_PATH_MAX];
991 mutt_mktemp (tmpfname);
992 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
993 mutt_perror (tmpfname);
996 mutt_unlink (tmpfname);
999 mutt_mktemp (certfile);
1000 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
1002 mutt_perror (certfile);
1006 /* Extract signer's certificate
1008 if ((thepid = smime_invoke (NULL, NULL, NULL,
1009 -1, -1, fileno (fperr),
1010 infile, NULL, NULL, NULL, certfile, NULL,
1011 SmimeGetSignerCertCommand)) == -1) {
1012 mutt_any_key_to_continue (_
1013 ("Error: unable to create OpenSSL subprocess!"));
1016 mutt_unlink (pk7out);
1017 mutt_unlink (certfile);
1021 mutt_wait_filter (thepid);
1027 empty = (fgetc (fpout) == EOF);
1030 mutt_copy_stream (fperr, stdout);
1031 mutt_any_key_to_continue (NULL);
1034 mutt_unlink (certfile);
1041 return m_strdup(certfile);
1047 /* Add a certificate and update index file (externally). */
1049 void smime_invoke_import (char *infile, char *mailbox __attribute__ ((unused)))
1051 char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1052 FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1055 mutt_mktemp (tmpfname);
1056 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1057 mutt_perror (tmpfname);
1060 mutt_unlink (tmpfname);
1062 mutt_mktemp (tmpfname);
1063 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
1065 mutt_perror (tmpfname);
1068 mutt_unlink (tmpfname);
1072 if (option (OPTASKCERTLABEL))
1073 mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1076 if ((certfile = smime_extract_certificate (infile))) {
1079 if ((thepid = smime_invoke (&smimein, NULL, NULL,
1080 -1, fileno (fpout), fileno (fperr),
1081 certfile, NULL, NULL, NULL, NULL, NULL,
1082 SmimeImportCertCommand)) == -1) {
1083 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1086 fputs (buf, smimein);
1087 fputc ('\n', smimein);
1090 mutt_wait_filter (thepid);
1092 mutt_unlink (certfile);
1093 p_delete(&certfile);
1101 mutt_copy_stream (fpout, stdout);
1102 mutt_copy_stream (fperr, stdout);
1111 int smime_verify_sender (HEADER * h)
1113 char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1117 mutt_mktemp (tempfname);
1118 if (!(fpout = safe_fopen (tempfname, "w"))) {
1119 mutt_perror (tempfname);
1123 if (h->security & ENCRYPT)
1124 mutt_copy_message (fpout, Context, h,
1125 M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1126 CH_MIME | CH_WEED | CH_NONEWLINE);
1128 mutt_copy_message (fpout, Context, h, 0, 0);
1134 h->env->from = mutt_expand_aliases (h->env->from);
1135 mbox = h->env->from->mailbox;
1137 else if (h->env->sender) {
1138 h->env->sender = mutt_expand_aliases (h->env->sender);
1139 mbox = h->env->sender->mailbox;
1143 if ((certfile = smime_extract_signer_certificate (tempfname))) {
1144 mutt_unlink (tempfname);
1145 if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1147 mutt_any_key_to_continue (NULL);
1151 mutt_unlink (certfile);
1152 p_delete(&certfile);
1155 mutt_any_key_to_continue (_("no certfile"));
1158 mutt_any_key_to_continue (_("no mbox"));
1160 mutt_unlink (tempfname);
1173 * Creating S/MIME - bodies.
1180 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1181 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1182 int smimeerrfd, const char *fname,
1185 return smime_invoke (smimein, smimeout, smimeerr,
1186 smimeinfd, smimeoutfd, smimeerrfd,
1187 fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1188 SmimeEncryptCommand);
1193 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1194 int smimeinfd, int smimeoutfd, int smimeerrfd,
1197 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1198 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1199 SmimeCertToUse, SmimeIntermediateToUse,
1206 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1208 char buf[LONG_STRING], certfile[LONG_STRING];
1209 char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1210 char smimeinfile[_POSIX_PATH_MAX];
1211 char *cert_start = certlist, *cert_end = certlist;
1212 FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1217 mutt_mktemp (tempfile);
1218 if ((fpout = safe_fopen (tempfile, "w+")) == NULL) {
1219 mutt_perror (tempfile);
1223 mutt_mktemp (smimeerrfile);
1224 if ((smimeerr = safe_fopen (smimeerrfile, "w+")) == NULL) {
1225 mutt_perror (smimeerrfile);
1227 mutt_unlink (tempfile);
1230 mutt_unlink (smimeerrfile);
1232 mutt_mktemp (smimeinfile);
1233 if ((fptmp = safe_fopen (smimeinfile, "w+")) == NULL) {
1234 mutt_perror (smimeinfile);
1235 mutt_unlink (tempfile);
1243 int off = m_strlen(certfile);
1245 while (*++cert_end && *cert_end != '\n');
1249 snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1250 NONULL (SmimeCertificates), cert_start);
1252 cert_start = cert_end;
1256 /* write a MIME entity */
1257 mutt_write_mime_header (a, fptmp);
1258 fputc ('\n', fptmp);
1259 mutt_write_mime_body (a, fptmp);
1263 smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1264 fileno (fpout), fileno (smimeerr),
1265 smimeinfile, certfile)) == -1) {
1267 mutt_unlink (smimeinfile);
1268 mutt_unlink (certfile);
1274 mutt_wait_filter (thepid);
1275 mutt_unlink (smimeinfile);
1276 mutt_unlink (certfile);
1280 empty = (fgetc (fpout) == EOF);
1285 while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1287 fputs (buf, stdout);
1291 /* pause if there is any error output from SMIME */
1293 mutt_any_key_to_continue (NULL);
1296 /* fatal error while trying to encrypt message */
1298 mutt_any_key_to_continue _("No output from OpenSSL..");
1300 mutt_unlink (tempfile);
1305 t->type = TYPEAPPLICATION;
1306 t->subtype = m_strdup("x-pkcs7-mime");
1307 parameter_setval(&t->parameter, "name", "smime.p7m");
1308 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
1309 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
1311 t->disposition = DISPATTACH;
1312 t->d_filename = m_strdup("smime.p7m");
1313 t->filename = m_strdup(tempfile);
1314 t->unlink = 1; /*delete after sending the message */
1324 BODY *smime_sign_message (BODY * a)
1327 char buffer[LONG_STRING];
1328 char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1329 FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1333 char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1335 if (!intermediates) {
1336 mutt_message (_("Warning: Intermediate certificate not found."));
1337 intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
1340 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
1342 mutt_mktemp (filetosign);
1343 if ((sfp = safe_fopen (filetosign, "w+")) == NULL) {
1344 mutt_perror (filetosign);
1348 mutt_mktemp (signedfile);
1349 if ((smimeout = safe_fopen (signedfile, "w+")) == NULL) {
1350 mutt_perror (signedfile);
1352 mutt_unlink (filetosign);
1356 mutt_write_mime_header (a, sfp);
1358 mutt_write_mime_body (a, sfp);
1363 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1364 NONULL (SmimeKeys), SmimeDefaultKey);
1366 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1367 NONULL (SmimeCertificates), SmimeDefaultKey);
1369 snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1370 NONULL (SmimeCertificates), intermediates);
1374 if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1375 -1, fileno (smimeout), -1,
1376 filetosign)) == -1) {
1377 mutt_perror (_("Can't open OpenSSL subprocess!"));
1380 mutt_unlink (signedfile);
1381 mutt_unlink (filetosign);
1384 fputs (SmimePass, smimein);
1385 fputc ('\n', smimein);
1389 mutt_wait_filter (thepid);
1391 /* check for errors from OpenSSL */
1395 while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1397 fputs (buffer, stdout);
1404 empty = (fgetc (smimeout) == EOF);
1407 mutt_unlink (filetosign);
1411 mutt_any_key_to_continue (NULL);
1414 mutt_any_key_to_continue _("No output from OpenSSL...");
1416 mutt_unlink (signedfile);
1417 return (NULL); /* fatal error while signing */
1421 t->type = TYPEMULTIPART;
1422 t->subtype = m_strdup("signed");
1423 t->encoding = ENC7BIT;
1425 t->disposition = DISPINLINE;
1427 parameter_set_boundary(&t->parameter);
1428 /* check if this can be extracted from private key somehow.... */
1429 parameter_setval(&t->parameter, "micalg", "sha1");
1430 parameter_setval(&t->parameter, "protocol",
1431 "application/x-pkcs7-signature");
1436 t->parts->next = body_new();
1438 t->type = TYPEAPPLICATION;
1439 t->subtype = m_strdup("x-pkcs7-signature");
1440 t->filename = m_strdup(signedfile);
1441 t->d_filename = m_strdup("smime.p7s");
1443 t->disposition = DISPATTACH;
1444 t->encoding = ENCBASE64;
1445 t->unlink = 1; /* ok to remove this file after sending. */
1453 * Handling S/MIME - bodies.
1458 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1459 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1460 int smimeerrfd, const char *fname,
1461 const char *sig_fname, int opaque)
1463 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1464 smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1465 (opaque ? SmimeVerifyOpaqueCommand :
1466 SmimeVerifyCommand));
1471 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1472 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1473 int smimeerrfd, const char *fname)
1475 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1476 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1477 SmimeCertToUse, NULL, SmimeDecryptCommand);
1482 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1484 char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1485 FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1490 ssize_t tmplength = 0;
1491 int origType = sigbdy->type;
1492 char *savePrefix = NULL;
1495 snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1497 /* decode to a tempfile, saving the original destination */
1499 if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1500 mutt_perror (signedfile);
1503 /* decoding the attachment changes the size and offset, so save a copy
1504 * of the "real" values now, and restore them after processing
1506 tmplength = sigbdy->length;
1507 tmpoffset = sigbdy->offset;
1509 /* if we are decoding binary bodies, we don't want to prefix each
1510 * line with the prefix or else the data will get corrupted.
1512 savePrefix = s->prefix;
1515 mutt_decode_attachment (sigbdy, s);
1517 sigbdy->length = ftello (s->fpout);
1521 /* restore final destination and substitute the tempfile for input */
1524 s->fpin = fopen (signedfile, "r");
1526 /* restore the prefix */
1527 s->prefix = savePrefix;
1529 sigbdy->type = origType;
1532 mutt_mktemp (smimeerrfile);
1533 if (!(smimeerr = safe_fopen (smimeerrfile, "w+"))) {
1534 mutt_perror (smimeerrfile);
1535 mutt_unlink (signedfile);
1539 crypt_current_time (s, "OpenSSL");
1541 if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1542 -1, -1, fileno (smimeerr),
1543 tempfile, signedfile, 0)) != -1) {
1547 if (mutt_wait_filter (thepid))
1557 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1558 if (linelen && !m_strcasecmp(line, "verification successful"))
1567 mutt_copy_stream (smimeerr, s->fpout);
1570 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1572 mutt_unlink (signedfile);
1573 mutt_unlink (smimeerrfile);
1575 sigbdy->length = tmplength;
1576 sigbdy->offset = tmpoffset;
1578 /* restore the original source stream */
1591 This handles application/pkcs7-mime which can either be a signed
1592 or an encrypted message.
1595 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1600 char buf[HUGE_STRING];
1601 char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1602 char tmpfname[_POSIX_PATH_MAX];
1603 char tmptmpfname[_POSIX_PATH_MAX];
1604 FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1605 FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1609 unsigned int type = mutt_is_application_smime (m);
1611 if (!(type & APPLICATION_SMIME))
1614 mutt_mktemp (outfile);
1615 if ((smimeout = safe_fopen (outfile, "w+")) == NULL) {
1616 mutt_perror (outfile);
1620 mutt_mktemp (errfile);
1621 if ((smimeerr = safe_fopen (errfile, "w+")) == NULL) {
1622 mutt_perror (errfile);
1627 mutt_unlink (errfile);
1630 mutt_mktemp (tmpfname);
1631 if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL) {
1632 mutt_perror (tmpfname);
1640 fseeko (s->fpin, m->offset, 0);
1641 last_pos = m->offset;
1643 mutt_copy_bytes (s->fpin, tmpfp, m->length);
1648 if ((type & ENCRYPT) &&
1649 (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1650 fileno (smimeout), fileno (smimeerr),
1654 mutt_unlink (tmpfname);
1655 if (s->flags & M_DISPLAY)
1656 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1659 else if ((type & SIGNOPAQUE) &&
1660 (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1662 fileno (smimeerr), NULL, tmpfname,
1663 SIGNOPAQUE)) == -1) {
1666 mutt_unlink (tmpfname);
1667 if (s->flags & M_DISPLAY)
1668 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1673 if (type & ENCRYPT) {
1674 if (!smime_valid_passphrase ())
1675 smime_void_passphrase ();
1676 fputs (SmimePass, smimein);
1677 fputc ('\n', smimein);
1682 mutt_wait_filter (thepid);
1683 mutt_unlink (tmpfname);
1686 if (s->flags & M_DISPLAY) {
1689 if ((c = fgetc (smimeerr)) != EOF) {
1690 ungetc (c, smimeerr);
1692 crypt_current_time (s, "OpenSSL");
1693 mutt_copy_stream (smimeerr, s->fpout);
1694 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1698 state_attach_puts (_("[-- The following data is S/MIME"
1699 " encrypted --]\n"), s);
1701 state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1712 mutt_mktemp (tmptmpfname);
1713 if ((fpout = safe_fopen (tmptmpfname, "w+")) == NULL) {
1714 mutt_perror (tmptmpfname);
1720 while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1721 len = m_strlen(buf);
1722 if (len > 1 && buf[len - 2] == '\r') {
1723 buf[len - 2] = '\n';
1724 buf[len - 1] = '\0';
1732 if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1733 fstat (fileno (fpout), &info);
1734 p->length = info.st_size - p->offset;
1736 mutt_parse_part (fpout, p);
1739 tmpfp_buffer = s->fpin;
1741 mutt_body_handler (p, s);
1742 s->fpin = tmpfp_buffer;
1748 mutt_unlink (outfile);
1752 mutt_unlink (tmptmpfname);
1757 if (s->flags & M_DISPLAY) {
1759 state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1761 state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1764 if (type & SIGNOPAQUE) {
1771 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1772 if (linelen && !m_strcasecmp(line, "verification successful"))
1777 m->goodsig = p->goodsig;
1778 m->badsig = p->badsig;
1789 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1793 char tempfile[_POSIX_PATH_MAX];
1795 long tmpoffset = b->offset;
1796 ssize_t tmplength = b->length;
1797 int origType = b->type;
1801 if (!mutt_is_application_smime (b))
1809 fseeko (s.fpin, b->offset, 0);
1811 mutt_mktemp (tempfile);
1812 if ((tmpfp = safe_fopen (tempfile, "w+")) == NULL) {
1813 mutt_perror (tempfile);
1817 mutt_unlink (tempfile);
1819 mutt_decode_attachment (b, &s);
1821 b->length = ftello (s.fpout);
1827 mutt_mktemp (tempfile);
1828 if ((*fpout = safe_fopen (tempfile, "w+")) == NULL) {
1829 mutt_perror (tempfile);
1833 mutt_unlink (tempfile);
1835 if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1840 (*cur)->goodsig = b->goodsig;
1841 (*cur)->badsig = b->badsig;
1845 b->length = tmplength;
1846 b->offset = tmpoffset;
1848 safe_fclose (&tmpfp);
1855 int smime_application_smime_handler (BODY * m, STATE * s)
1857 return smime_handle_entity (m, s, NULL) ? 0 : -1;
1860 int smime_send_menu (HEADER * msg, int *redraw)
1864 switch (mutt_multi_choice
1865 (_("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1867 case 1: /* (e)ncrypt */
1868 msg->security |= ENCRYPT;
1869 msg->security &= ~SIGN;
1872 case 3: /* encrypt (w)ith */
1875 msg->security |= ENCRYPT;
1878 /* I use "dra" because "123" is recognized anyway */
1879 switch (mutt_multi_choice (_("Choose algorithm family:"
1880 " 1: DES, 2: RC2, 3: AES,"
1881 " or (c)lear? "), _("drac"))) {
1883 switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
1886 m_strreplace(&SmimeCryptAlg, "des");
1889 m_strreplace(&SmimeCryptAlg, "des3");
1895 switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
1898 m_strreplace(&SmimeCryptAlg, "rc2-40");
1901 m_strreplace(&SmimeCryptAlg, "rc2-64");
1904 m_strreplace(&SmimeCryptAlg, "rc2-128");
1910 switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
1913 m_strreplace(&SmimeCryptAlg, "aes128");
1916 m_strreplace(&SmimeCryptAlg, "aes192");
1919 m_strreplace(&SmimeCryptAlg, "aes256");
1924 case 4: /* (c)lear */
1925 p_delete(&SmimeCryptAlg);
1927 case -1: /* Ctrl-G or Enter */
1931 } while (choice == -1);
1935 case 2: /* (s)ign */
1937 if (!SmimeDefaultKey)
1938 mutt_message (_("Can't sign: No key specified. Use Sign As."));
1941 msg->security |= SIGN;
1942 msg->security &= ~ENCRYPT;
1946 case 4: /* sign (a)s */
1948 if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1949 m_strreplace(&SmimeDefaultKey, p);
1951 msg->security |= SIGN;
1953 /* probably need a different passphrase */
1954 crypt_smime_void_passphrase ();
1958 msg->security &= ~SIGN;
1961 *redraw = REDRAW_FULL;
1964 case 5: /* (b)oth */
1965 msg->security |= (ENCRYPT | SIGN);
1968 case 6: /* (f)orget it */
1969 case 7: /* (c)lear */
1974 if (msg->security && msg->security != APPLICATION_SMIME)
1975 msg->security |= APPLICATION_SMIME;
1979 return (msg->security);