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.
12 #include <lib-lib/lib-lib.h>
14 #ifdef HAVE_SYS_RESOURCE_H
15 # include <sys/resource.h>
18 #include <lib-mime/mime.h>
19 #include <lib-sys/unix.h>
21 #include <lib-ui/curses.h>
22 #include <lib-ui/enter.h>
23 #include <lib-ui/menu.h>
33 struct smime_command_context {
34 const char *key; /* %k */
35 const char *cryptalg; /* %a */
36 const char *fname; /* %f */
37 const char *sig_fname; /* %s */
38 const char *certificates; /* %c */
39 const char *intermediates; /* %i */
48 char trust; /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
49 short public; /* 1=public 0=private */
53 char SmimePass[STRING];
54 time_t SmimeExptime = 0; /* when does the cached passphrase expire? */
57 static char SmimeKeyToUse[_POSIX_PATH_MAX] = { 0 };
58 static char SmimeCertToUse[_POSIX_PATH_MAX];
59 static char SmimeIntermediateToUse[_POSIX_PATH_MAX];
63 * Create a format string to be used with scanf.
64 * To use it, write, for instance, MUTT_FORMAT(HUGE_STRING).
66 * See K&R 2nd ed, p. 231 for an explanation.
68 #define _MUTT_FORMAT_2(a,b) "%" a b
69 #define _MUTT_FORMAT_1(a, b) _MUTT_FORMAT_2(#a, b)
70 #define MUTT_FORMAT(a) _MUTT_FORMAT_1(a, "s")
74 * Queries and passphrase handling.
78 /* these are copies from pgp.c */
81 void smime_void_passphrase (void)
83 p_clear(SmimePass, countof(SmimePass));
87 int smime_valid_passphrase (void)
89 time_t now = time (NULL);
91 if (now < SmimeExptime)
92 /* Use cached copy. */
95 smime_void_passphrase ();
97 if (mutt_get_field_unbuffered (_("Enter S/MIME passphrase:"), SmimePass,
98 sizeof (SmimePass), M_PASS) == 0) {
99 SmimeExptime = time (NULL) + SmimeTimeout;
110 * The OpenSSL interface
113 /* This is almost identical to ppgp's invoking interface. */
116 _mutt_fmt_smime_command (char *dest, ssize_t destlen, char op,
117 const char *src, const char *prefix,
118 const char *ifstring, const char *elsestring,
119 unsigned long data, format_flag flags)
122 struct smime_command_context *cctx = (struct smime_command_context *) data;
123 int optional = (flags & M_FORMAT_OPTIONAL);
129 char path[_POSIX_PATH_MAX];
130 char buf1[LONG_STRING], buf2[LONG_STRING];
133 m_strcpy(path, sizeof(path), NONULL(SmimeCALocation));
134 mutt_expand_path (path, sizeof (path));
135 mutt_quote_filename (buf1, sizeof (buf1), path);
137 if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
138 snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
140 snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
142 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
143 snprintf (dest, destlen, fmt, buf2);
145 else if (!SmimeCALocation)
151 { /* certificate (list) */
153 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
154 snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
156 else if (!cctx->certificates)
162 { /* intermediate certificates */
164 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
165 snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
167 else if (!cctx->intermediates)
173 { /* detached signature */
175 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
176 snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
178 else if (!cctx->sig_fname)
186 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
187 snprintf (dest, destlen, fmt, NONULL (cctx->key));
195 { /* algorithm for encryption */
197 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
198 snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
206 { /* file to process */
208 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
209 snprintf (dest, destlen, fmt, NONULL (cctx->fname));
211 else if (!cctx->fname)
222 mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
224 else if (flags & M_FORMAT_OPTIONAL)
225 mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
233 static void mutt_smime_command (char *d, ssize_t dlen,
234 struct smime_command_context *cctx,
237 mutt_FormatString (d, dlen, NONULL (fmt), _mutt_fmt_smime_command,
238 (unsigned long) cctx, 0);
241 static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
242 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
243 int smimeerrfd, const char *fname,
244 const char *sig_fname, const char *cryptalg,
245 const char *key, const char *certificates,
246 const char *intermediates, const char *format)
248 struct smime_command_context cctx;
249 char cmd[HUGE_STRING];
253 if (!format || !*format)
257 cctx.sig_fname = sig_fname;
259 cctx.cryptalg = cryptalg;
260 cctx.certificates = certificates;
261 cctx.intermediates = intermediates;
263 mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
265 return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
266 smimeinfd, smimeoutfd, smimeerrfd);
275 * Key and certificate handling.
281 Search the certificate index for given mailbox.
282 return certificate file name.
285 static void smime_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
287 smime_id *Table = (smime_id *) menu->data;
288 smime_id this = Table[num];
289 const char *truststate;
291 switch (this.trust) {
293 truststate = N_("Trusted ");
296 truststate = N_("Verified ");
299 truststate = N_("Unverified");
302 truststate = N_("Expired ");
305 truststate = N_("Revoked ");
308 truststate = N_("Invalid ");
311 truststate = N_("Unknown ");
314 snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
315 truststate, this.email, this.nick);
317 snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
318 this.email, this.nick);
325 char *smime_ask_for_key (char *prompt, char *mailbox __attribute__((unused)),
330 long cert_num; /* Will contain the number of certificates.
331 * To be able to get it, the .index file will be read twice... */
332 char index_file[_POSIX_PATH_MAX];
334 char buf[LONG_STRING];
335 char fields[5][STRING];
336 int numFields, hash_suffix, done, cur; /* The current entry */
339 char helpstr[HUGE_STRING * 3];
344 prompt = _("Enter keyID: ");
345 snprintf (index_file, sizeof (index_file), "%s/.index",
346 public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));
348 idx = fopen (index_file, "r");
350 mutt_perror (index_file);
355 while (!feof (idx)) {
356 if (fgets (buf, sizeof (buf), idx))
363 if (mutt_get_field (prompt, qry, sizeof (qry), 0))
365 snprintf (title, sizeof (title),
366 _("S/MIME certificates matching \"%s\"."), qry);
369 idx = fopen (index_file, "r");
371 mutt_perror (index_file);
376 Table = p_new(smime_id, cert_num);
377 while (!feof (idx)) {
379 fscanf (idx, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
380 fields[0], &hash, &hash_suffix, fields[2]);
382 fscanf (idx, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
383 fields[3], fields[4]);
385 /* 0=email 1=name 2=nick 3=intermediate 4=trust */
389 /* Check if query matches this certificate */
390 if (!m_stristr(fields[0], qry) && !m_stristr(fields[2], qry))
393 Table[cur].hash = hash;
394 Table[cur].suffix = hash_suffix;
395 m_strcpy(Table[cur].email, sizeof(Table[cur].email), fields[0]);
396 m_strcpy(Table[cur].nick, sizeof(Table[cur].nick), fields[2]);
397 Table[cur].trust = *fields[4];
398 Table[cur].public = public;
404 /* Make Helpstring */
406 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT);
407 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
408 mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME,
409 OP_GENERIC_SELECT_ENTRY);
410 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
411 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
412 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
414 /* Create the menu */
415 menu = mutt_new_menu ();
417 menu->make_entry = smime_entry;
418 menu->menu = MENU_SMIME;
419 menu->help = helpstr;
422 /* sorting keys might be done later - TODO */
429 switch (mutt_menuLoop (menu)) {
430 case OP_GENERIC_SELECT_ENTRY:
442 fname = p_new(char, 13); /* Hash + '.' + Suffix + \0 */
443 sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
448 mutt_menuDestroy (&menu);
450 set_option (OPTNEEDREDRAW);
459 char *smime_get_field_from_db (char *mailbox, char *query, short public,
462 int addr_len, query_len, found = 0, ask = 0, choice = 0;
463 char cert_path[_POSIX_PATH_MAX];
464 char buf[LONG_STRING], prompt[STRING];
465 char fields[5][STRING];
469 char key_trust_level = 0;
472 if (!mailbox && !query)
475 addr_len = mailbox ? m_strlen(mailbox) : 0;
476 query_len = query ? m_strlen(query) : 0;
480 /* index-file format:
481 mailbox certfile label issuer_certfile trust_flags\n
483 certfile is a hash value generated by openssl.
484 Note that this was done according to the OpenSSL
485 specs on their CA-directory.
488 snprintf (cert_path, sizeof (cert_path), "%s/.index",
489 (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));
491 if (!stat (cert_path, &info)) {
492 if ((fp = safe_fopen (cert_path, "r")) == NULL) {
493 mutt_perror (cert_path);
497 while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
498 if (mailbox && !(m_strncasecmp(mailbox, buf, addr_len))) {
499 numFields = sscanf (buf,
500 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
501 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
502 MUTT_FORMAT (STRING) "\n",
503 fields[0], fields[1],
504 fields[2], fields[3], fields[4]);
507 if (mailbox && public &&
509 *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
513 if (public && *fields[4] == 'u')
514 snprintf (prompt, sizeof (prompt),
516 ("ID %s is unverified. Do you want to use it for %s ?"),
518 else if (public && *fields[4] == 'v')
519 snprintf (prompt, sizeof (prompt),
520 _("Use (untrusted!) ID %s for %s ?"),
523 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
527 if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
533 else if (choice == M_NO) {
537 else if (choice == M_YES) {
538 m_strcpy(key, sizeof(key), fields[1]);
545 key_trust_level = *fields[4];
546 m_strcpy(key, sizeof(key), fields[1]);
551 numFields = sscanf (buf,
552 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
553 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
554 MUTT_FORMAT (STRING) "\n",
555 fields[0], fields[1],
556 fields[2], fields[3], fields[4]);
558 /* query = label: return certificate. */
559 if (numFields >= 3 &&
560 !(m_strncasecmp(query, fields[2], query_len))) {
562 m_strcpy(key, sizeof(key), fields[1]);
564 /* query = certificate: return intermediate certificate. */
565 else if (numFields >= 4 &&
566 !(m_strncasecmp(query, fields[1], query_len))) {
568 m_strcpy(key, sizeof(key), fields[3]);
575 if (public && *fields[4] == 'u')
576 snprintf (prompt, sizeof (prompt),
577 _("ID %s is unverified. Do you want to use it for %s ?"),
579 else if (public && *fields[4] == 'v')
580 snprintf (prompt, sizeof (prompt),
581 _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
583 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
585 choice = mutt_yesorno (prompt, M_NO);
586 if (choice == -1 || choice == M_NO)
589 else if (key_trust_level && may_ask) {
590 if (key_trust_level == 'u') {
591 snprintf (prompt, sizeof (prompt),
592 _("ID %s is unverified. Do you want to use it for %s ?"),
594 choice = mutt_yesorno (prompt, M_NO);
598 else if (key_trust_level == 'v') {
600 ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
608 /* Note: m_strdup("") returns NULL. */
609 return m_strdup(key);
613 This sets the '*ToUse' variables for an upcoming decryption, where
614 the reuquired key is different from SmimeDefaultKey.
616 static void _smime_getkeys (char *mailbox)
621 k = smime_get_field_from_db (mailbox, NULL, 0, 1);
624 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
625 k = smime_ask_for_key (buf, mailbox, 0);
629 /* the key used last time. */
630 if (*SmimeKeyToUse &&
631 !m_strcasecmp(k, SmimeKeyToUse + m_strlen(SmimeKeys) + 1)) {
636 smime_void_passphrase ();
638 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
639 NONULL (SmimeKeys), k);
641 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
642 NONULL (SmimeCertificates), k);
644 if (m_strcasecmp(k, SmimeDefaultKey))
645 smime_void_passphrase ();
651 if (*SmimeKeyToUse) {
652 if (!m_strcasecmp(SmimeDefaultKey,
653 SmimeKeyToUse + m_strlen(SmimeKeys) + 1))
656 smime_void_passphrase ();
659 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
660 NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
662 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
663 NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
666 void smime_getkeys (ENVELOPE * env)
671 if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
672 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
673 NONULL (SmimeKeys), SmimeDefaultKey);
675 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
676 NONULL (SmimeCertificates), SmimeDefaultKey);
681 for (t = env->to; !found && t; t = t->next)
682 if (mutt_addr_is_user (t)) {
684 _smime_getkeys (t->mailbox);
686 for (t = env->cc; !found && t; t = t->next)
687 if (mutt_addr_is_user (t)) {
689 _smime_getkeys (t->mailbox);
691 if (!found && (t = mutt_default_from ())) {
692 _smime_getkeys (t->mailbox);
693 address_list_wipe(&t);
697 /* This routine attempts to find the keyids of the recipients of a message.
698 * It returns NULL if any of the keys can not be found.
701 char *smime_findKeys (address_t * to, address_t * cc, address_t * bcc)
703 char *keyID, *keylist = NULL;
704 ssize_t keylist_size = 0;
705 ssize_t keylist_used = 0;
706 address_t *tmp = NULL, *addr = NULL;
707 address_t **last = &tmp;
711 const char *fqdn = mutt_fqdn (1);
713 for (i = 0; i < 3; i++) {
728 *last = address_list_dup (p);
730 last = &((*last)->next);
734 rfc822_qualify (tmp, fqdn);
736 address_list_uniq(tmp);
738 for (p = tmp; p; p = p->next) {
739 char buf[LONG_STRING];
743 if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
744 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
745 keyID = smime_ask_for_key (buf, q->mailbox, 1);
748 mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
750 address_list_wipe(&tmp);
751 address_list_wipe(&addr);
755 keylist_size += m_strlen(keyID) + 2;
756 p_realloc(&keylist, keylist_size);
757 sprintf (keylist + keylist_used, "%s\n", keyID); /* __SPRINTF_CHECKED__ */
758 keylist_used = m_strlen(keylist);
760 address_list_wipe(&addr);
763 address_list_wipe(&tmp);
772 static int smime_handle_cert_email (char *certificate, char *mailbox,
773 int copy, char ***buffer, int *num)
775 FILE *fpout = NULL, *fperr = NULL;
776 char tmpfname[_POSIX_PATH_MAX];
778 int ret = -1, count = 0;
781 fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
783 mutt_perror (tmpfname);
786 mutt_unlink (tmpfname);
788 fpout = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
791 mutt_perror (tmpfname);
794 mutt_unlink (tmpfname);
796 if ((thepid = smime_invoke (NULL, NULL, NULL,
797 -1, fileno (fpout), fileno (fperr),
798 certificate, NULL, NULL, NULL, NULL, NULL,
799 SmimeGetCertEmailCommand)) == -1) {
800 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
806 mutt_wait_filter (thepid);
814 while ((fgets (email, sizeof (email), fpout))) {
815 *(email + m_strlen(email) - 1) = '\0';
816 if (m_strncasecmp(email, mailbox, m_strlen(mailbox)) == 0)
819 ret = ret < 0 ? 0 : ret;
825 mutt_copy_stream (fperr, stdout);
826 mutt_any_key_to_continue (_
827 ("Error: unable to create OpenSSL subprocess!"));
835 if (copy && buffer && num) {
837 *buffer = p_new(char *, count);
841 while ((fgets (email, sizeof (email), fpout))) {
842 *(email + m_strlen(email) - 1) = '\0';
843 (*buffer)[count] = p_dupstr(email, m_strlen(email));
858 static char *smime_extract_certificate (char *infile)
860 FILE *fpout = NULL, *fperr = NULL;
861 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
862 char tmpfname[_POSIX_PATH_MAX];
867 fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
869 mutt_perror (tmpfname);
872 mutt_unlink (tmpfname);
874 fpout = m_tempfile (pk7out, sizeof(tmpfname), NONULL(Tempdir), NULL);
877 mutt_perror (pk7out);
881 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
882 extract the full set of certificates directly.
884 if ((thepid = smime_invoke (NULL, NULL, NULL,
885 -1, fileno (fpout), fileno (fperr),
886 infile, NULL, NULL, NULL, NULL, NULL,
887 SmimePk7outCommand)) == -1) {
888 mutt_any_key_to_continue (_
889 ("Error: unable to create OpenSSL subprocess!"));
892 mutt_unlink (pk7out);
896 mutt_wait_filter (thepid);
904 empty = (fgetc (fpout) == EOF);
909 mutt_perror (pk7out);
910 mutt_copy_stream (fperr, stdout);
912 mutt_unlink (pk7out);
916 fpout = m_tempfile (certfile, sizeof(certfile), NONULL(Tempdir), NULL);
919 mutt_unlink (pk7out);
920 mutt_perror (certfile);
924 /* Step 2: Extract the certificates from a PKCS#7 structure.
926 if ((thepid = smime_invoke (NULL, NULL, NULL,
927 -1, fileno (fpout), fileno (fperr),
928 pk7out, NULL, NULL, NULL, NULL, NULL,
929 SmimeGetCertCommand)) == -1) {
930 mutt_any_key_to_continue (_
931 ("Error: unable to create OpenSSL subprocess!"));
934 mutt_unlink (pk7out);
935 mutt_unlink (certfile);
939 mutt_wait_filter (thepid);
941 mutt_unlink (pk7out);
947 empty = (fgetc (fpout) == EOF);
949 mutt_copy_stream (fperr, stdout);
952 mutt_unlink (certfile);
959 return m_strdup(certfile);
962 static char *smime_extract_signer_certificate (char *infile)
964 FILE *fpout = NULL, *fperr = NULL;
965 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
966 char tmpfname[_POSIX_PATH_MAX];
970 fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
972 mutt_perror (tmpfname);
975 mutt_unlink (tmpfname);
977 m_tempfile (certfile, sizeof(certfile), NONULL(Tempdir), NULL);
980 mutt_perror (certfile);
984 /* Extract signer's certificate
986 if ((thepid = smime_invoke (NULL, NULL, NULL,
987 -1, -1, fileno (fperr),
988 infile, NULL, NULL, NULL, certfile, NULL,
989 SmimeGetSignerCertCommand)) == -1) {
990 mutt_any_key_to_continue (_
991 ("Error: unable to create OpenSSL subprocess!"));
994 mutt_unlink (pk7out);
995 mutt_unlink (certfile);
999 mutt_wait_filter (thepid);
1005 empty = (fgetc (fpout) == EOF);
1010 mutt_copy_stream (fperr, stdout);
1011 mutt_any_key_to_continue (NULL);
1013 mutt_unlink (certfile);
1019 return m_strdup(certfile);
1022 /* Add a certificate and update index file (externally). */
1024 void smime_invoke_import (char *infile, char *mailbox __attribute__ ((unused)))
1026 char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1027 FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1030 fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1032 mutt_perror (tmpfname);
1035 mutt_unlink (tmpfname);
1037 fpout = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1040 mutt_perror (tmpfname);
1043 mutt_unlink (tmpfname);
1046 if (option (OPTASKCERTLABEL))
1047 mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1050 if ((certfile = smime_extract_certificate (infile))) {
1053 if ((thepid = smime_invoke (&smimein, NULL, NULL,
1054 -1, fileno (fpout), fileno (fperr),
1055 certfile, NULL, NULL, NULL, NULL, NULL,
1056 SmimeImportCertCommand)) == -1) {
1057 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1060 fputs (buf, smimein);
1061 fputc ('\n', smimein);
1064 mutt_wait_filter (thepid);
1066 mutt_unlink (certfile);
1067 p_delete(&certfile);
1075 mutt_copy_stream (fpout, stdout);
1076 mutt_copy_stream (fperr, stdout);
1082 int smime_verify_sender (HEADER * h)
1084 char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1088 fpout = m_tempfile (tempfname, sizeof(tempfname), NONULL(Tempdir), NULL);
1090 mutt_perror (tempfname);
1094 if (h->security & ENCRYPT)
1095 mutt_copy_message (fpout, Context, h,
1096 M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1097 CH_MIME | CH_WEED | CH_NONEWLINE);
1099 mutt_copy_message (fpout, Context, h, 0, 0);
1105 h->env->from = mutt_expand_aliases (h->env->from);
1106 mbox = h->env->from->mailbox;
1108 else if (h->env->sender) {
1109 h->env->sender = mutt_expand_aliases (h->env->sender);
1110 mbox = h->env->sender->mailbox;
1114 if ((certfile = smime_extract_signer_certificate (tempfname))) {
1115 mutt_unlink (tempfname);
1116 if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1118 mutt_any_key_to_continue (NULL);
1122 mutt_unlink (certfile);
1123 p_delete(&certfile);
1126 mutt_any_key_to_continue (_("no certfile"));
1129 mutt_any_key_to_continue (_("no mbox"));
1131 mutt_unlink (tempfname);
1144 * Creating S/MIME - bodies.
1151 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1152 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1153 int smimeerrfd, const char *fname,
1156 return smime_invoke (smimein, smimeout, smimeerr,
1157 smimeinfd, smimeoutfd, smimeerrfd,
1158 fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1159 SmimeEncryptCommand);
1164 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1165 int smimeinfd, int smimeoutfd, int smimeerrfd,
1168 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1169 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1170 SmimeCertToUse, SmimeIntermediateToUse,
1177 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1179 char buf[LONG_STRING], certfile[LONG_STRING];
1180 char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1181 char smimeinfile[_POSIX_PATH_MAX];
1182 char *cert_start = certlist, *cert_end = certlist;
1183 FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1188 fpout = m_tempfile (tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
1190 mutt_perror (tempfile);
1194 smimeerr = m_tempfile (smimeerrfile, sizeof(smimeerrfile), NONULL(Tempdir), NULL);
1196 mutt_perror (smimeerrfile);
1198 mutt_unlink (tempfile);
1201 mutt_unlink (smimeerrfile);
1203 fptmp = m_tempfile (smimeinfile, sizeof(smimeinfile), NONULL(Tempdir), NULL);
1205 mutt_perror (smimeinfile);
1206 mutt_unlink (tempfile);
1208 m_fclose(&smimeerr);
1214 int off = m_strlen(certfile);
1216 while (*++cert_end && *cert_end != '\n');
1220 snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1221 NONULL (SmimeCertificates), cert_start);
1223 cert_start = cert_end;
1227 /* write a MIME entity */
1228 mutt_write_mime_header (a, fptmp);
1229 fputc ('\n', fptmp);
1230 mutt_write_mime_body (a, fptmp);
1234 smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1235 fileno (fpout), fileno (smimeerr),
1236 smimeinfile, certfile)) == -1) {
1237 m_fclose(&smimeerr);
1238 mutt_unlink (smimeinfile);
1239 mutt_unlink (certfile);
1245 mutt_wait_filter (thepid);
1246 mutt_unlink (smimeinfile);
1247 mutt_unlink (certfile);
1251 empty = (fgetc (fpout) == EOF);
1256 while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1258 fputs (buf, stdout);
1260 m_fclose(&smimeerr);
1262 /* pause if there is any error output from SMIME */
1264 mutt_any_key_to_continue (NULL);
1267 /* fatal error while trying to encrypt message */
1269 mutt_any_key_to_continue _("No output from OpenSSL..");
1271 mutt_unlink (tempfile);
1276 t->type = TYPEAPPLICATION;
1277 t->subtype = m_strdup("x-pkcs7-mime");
1278 parameter_setval(&t->parameter, "name", "smime.p7m");
1279 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
1280 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
1282 t->disposition = DISPATTACH;
1283 t->d_filename = m_strdup("smime.p7m");
1284 t->filename = m_strdup(tempfile);
1285 t->unlink = 1; /*delete after sending the message */
1295 BODY *smime_sign_message (BODY * a)
1298 char buffer[LONG_STRING];
1299 char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1300 FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1304 char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1306 if (!intermediates) {
1307 mutt_message (_("Warning: Intermediate certificate not found."));
1308 intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
1311 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
1313 sfp = m_tempfile (filetosign, sizeof(filetosign), NONULL(Tempdir), NULL);
1315 mutt_perror (filetosign);
1319 smimeout = m_tempfile (signedfile, sizeof(signedfile), NONULL(Tempdir), NULL);
1321 mutt_perror (signedfile);
1323 mutt_unlink (filetosign);
1327 mutt_write_mime_header (a, sfp);
1329 mutt_write_mime_body (a, sfp);
1334 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1335 NONULL (SmimeKeys), SmimeDefaultKey);
1337 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1338 NONULL (SmimeCertificates), SmimeDefaultKey);
1340 snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1341 NONULL (SmimeCertificates), intermediates);
1345 if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1346 -1, fileno (smimeout), -1,
1347 filetosign)) == -1) {
1348 mutt_perror (_("Can't open OpenSSL subprocess!"));
1350 m_fclose(&smimeout);
1351 mutt_unlink (signedfile);
1352 mutt_unlink (filetosign);
1355 fputs (SmimePass, smimein);
1356 fputc ('\n', smimein);
1360 mutt_wait_filter (thepid);
1362 /* check for errors from OpenSSL */
1366 while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1368 fputs (buffer, stdout);
1370 m_fclose(&smimeerr);
1375 empty = (fgetc (smimeout) == EOF);
1376 m_fclose(&smimeout);
1378 mutt_unlink (filetosign);
1382 mutt_any_key_to_continue (NULL);
1385 mutt_any_key_to_continue _("No output from OpenSSL...");
1387 mutt_unlink (signedfile);
1388 return (NULL); /* fatal error while signing */
1392 t->type = TYPEMULTIPART;
1393 t->subtype = m_strdup("signed");
1394 t->encoding = ENC7BIT;
1396 t->disposition = DISPINLINE;
1398 parameter_set_boundary(&t->parameter);
1399 /* check if this can be extracted from private key somehow.... */
1400 parameter_setval(&t->parameter, "micalg", "sha1");
1401 parameter_setval(&t->parameter, "protocol",
1402 "application/x-pkcs7-signature");
1407 t->parts->next = body_new();
1409 t->type = TYPEAPPLICATION;
1410 t->subtype = m_strdup("x-pkcs7-signature");
1411 t->filename = m_strdup(signedfile);
1412 t->d_filename = m_strdup("smime.p7s");
1414 t->disposition = DISPATTACH;
1415 t->encoding = ENCBASE64;
1416 t->unlink = 1; /* ok to remove this file after sending. */
1424 * Handling S/MIME - bodies.
1429 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1430 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1431 int smimeerrfd, const char *fname,
1432 const char *sig_fname, int opaque)
1434 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1435 smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1436 (opaque ? SmimeVerifyOpaqueCommand :
1437 SmimeVerifyCommand));
1442 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1443 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1444 int smimeerrfd, const char *fname)
1446 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1447 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1448 SmimeCertToUse, NULL, SmimeDecryptCommand);
1453 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1455 char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1456 FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1461 ssize_t tmplength = 0;
1462 int origType = sigbdy->type;
1463 char *savePrefix = NULL;
1466 snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1468 /* decode to a tempfile, saving the original destination */
1470 if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1471 mutt_perror (signedfile);
1474 /* decoding the attachment changes the size and offset, so save a copy
1475 * of the "real" values now, and restore them after processing
1477 tmplength = sigbdy->length;
1478 tmpoffset = sigbdy->offset;
1480 /* if we are decoding binary bodies, we don't want to prefix each
1481 * line with the prefix or else the data will get corrupted.
1483 savePrefix = s->prefix;
1486 mutt_decode_attachment (sigbdy, s);
1488 sigbdy->length = ftello (s->fpout);
1490 m_fclose(&s->fpout);
1492 /* restore final destination and substitute the tempfile for input */
1495 s->fpin = fopen (signedfile, "r");
1497 /* restore the prefix */
1498 s->prefix = savePrefix;
1500 sigbdy->type = origType;
1502 smimeerr = m_tempfile(smimeerrfile, sizeof(smimeerrfile), NONULL(Tempdir), NULL);
1504 mutt_perror (smimeerrfile);
1505 mutt_unlink (signedfile);
1509 crypt_current_time (s, "OpenSSL");
1511 if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1512 -1, -1, fileno (smimeerr),
1513 tempfile, signedfile, 0)) != -1) {
1514 m_fclose(&smimeout);
1516 if (mutt_wait_filter (thepid))
1526 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1527 if (linelen && !m_strcasecmp(line, "verification successful"))
1536 mutt_copy_stream (smimeerr, s->fpout);
1537 m_fclose(&smimeerr);
1539 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1541 mutt_unlink (signedfile);
1542 mutt_unlink (smimeerrfile);
1544 sigbdy->length = tmplength;
1545 sigbdy->offset = tmpoffset;
1547 /* restore the original source stream */
1560 This handles application/pkcs7-mime which can either be a signed
1561 or an encrypted message.
1564 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1569 char buf[HUGE_STRING];
1570 char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1571 char tmpfname[_POSIX_PATH_MAX];
1572 char tmptmpfname[_POSIX_PATH_MAX];
1573 FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1574 FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1578 unsigned int type = mutt_is_application_smime (m);
1580 if (!(type & APPLICATION_SMIME))
1583 smimeout = m_tempfile (outfile, sizeof(outfile), NONULL(Tempdir), NULL);
1585 mutt_perror (outfile);
1589 smimeerr = m_tempfile(errfile, sizeof(errfile), NONULL(Tempdir), NULL);
1591 mutt_perror (errfile);
1592 m_fclose(&smimeout);
1595 mutt_unlink (errfile);
1597 tmpfp = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1599 mutt_perror (tmpfname);
1600 m_fclose(&smimeout);
1601 m_fclose(&smimeerr);
1605 fseeko (s->fpin, m->offset, 0);
1606 last_pos = m->offset;
1608 mutt_copy_bytes (s->fpin, tmpfp, m->length);
1611 if ((type & ENCRYPT) &&
1612 (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1613 fileno (smimeout), fileno (smimeerr),
1615 m_fclose(&smimeout);
1616 mutt_unlink (tmpfname);
1617 if (s->flags & M_DISPLAY)
1618 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1621 else if ((type & SIGNOPAQUE) &&
1622 (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1624 fileno (smimeerr), NULL, tmpfname,
1625 SIGNOPAQUE)) == -1) {
1626 m_fclose(&smimeout);
1627 mutt_unlink (tmpfname);
1628 if (s->flags & M_DISPLAY)
1629 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1634 if (type & ENCRYPT) {
1635 if (!smime_valid_passphrase ())
1636 smime_void_passphrase ();
1637 fputs (SmimePass, smimein);
1638 fputc ('\n', smimein);
1643 mutt_wait_filter (thepid);
1644 mutt_unlink (tmpfname);
1647 if (s->flags & M_DISPLAY) {
1650 if ((c = fgetc (smimeerr)) != EOF) {
1651 ungetc (c, smimeerr);
1653 crypt_current_time (s, "OpenSSL");
1654 mutt_copy_stream (smimeerr, s->fpout);
1655 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1659 state_attach_puts (_("[-- The following data is S/MIME"
1660 " encrypted --]\n"), s);
1662 state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1673 fpout = m_tempfile (tmptmpfname, sizeof(tmptmpfname), NONULL(Tempdir), NULL);
1675 mutt_perror (tmptmpfname);
1676 m_fclose(&smimeout);
1680 while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1681 len = m_strlen(buf);
1682 if (len > 1 && buf[len - 2] == '\r') {
1683 buf[len - 2] = '\n';
1684 buf[len - 1] = '\0';
1692 if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1693 fstat (fileno (fpout), &info);
1694 p->length = info.st_size - p->offset;
1696 mutt_parse_part (fpout, p);
1699 tmpfp_buffer = s->fpin;
1701 mutt_body_handler (p, s);
1702 s->fpin = tmpfp_buffer;
1706 m_fclose(&smimeout);
1707 mutt_unlink (outfile);
1711 mutt_unlink (tmptmpfname);
1716 if (s->flags & M_DISPLAY) {
1718 state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1720 state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1723 if (type & SIGNOPAQUE) {
1730 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1731 if (linelen && !m_strcasecmp(line, "verification successful"))
1736 m->goodsig = p->goodsig;
1737 m->badsig = p->badsig;
1739 m_fclose(&smimeerr);
1748 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1752 char tempfile[_POSIX_PATH_MAX];
1754 long tmpoffset = b->offset;
1755 ssize_t tmplength = b->length;
1756 int origType = b->type;
1760 if (!mutt_is_application_smime (b))
1768 fseeko (s.fpin, b->offset, 0);
1770 tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
1772 mutt_perror (tempfile);
1776 mutt_unlink (tempfile);
1778 mutt_decode_attachment (b, &s);
1780 b->length = ftello (s.fpout);
1786 *fpout = m_tempfile (tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
1788 mutt_perror (tempfile);
1792 mutt_unlink (tempfile);
1794 if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1799 (*cur)->goodsig = b->goodsig;
1800 (*cur)->badsig = b->badsig;
1804 b->length = tmplength;
1805 b->offset = tmpoffset;
1814 int smime_application_smime_handler (BODY * m, STATE * s)
1816 return smime_handle_entity (m, s, NULL) ? 0 : -1;
1819 int smime_send_menu (HEADER * msg, int *redraw)
1823 switch (mutt_multi_choice
1824 (_("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1826 case 1: /* (e)ncrypt */
1827 msg->security |= ENCRYPT;
1828 msg->security &= ~SIGN;
1831 case 3: /* encrypt (w)ith */
1834 msg->security |= ENCRYPT;
1837 /* I use "dra" because "123" is recognized anyway */
1838 switch (mutt_multi_choice (_("Choose algorithm family:"
1839 " 1: DES, 2: RC2, 3: AES,"
1840 " or (c)lear? "), _("drac"))) {
1842 switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
1845 m_strreplace(&SmimeCryptAlg, "des");
1848 m_strreplace(&SmimeCryptAlg, "des3");
1854 switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
1857 m_strreplace(&SmimeCryptAlg, "rc2-40");
1860 m_strreplace(&SmimeCryptAlg, "rc2-64");
1863 m_strreplace(&SmimeCryptAlg, "rc2-128");
1869 switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
1872 m_strreplace(&SmimeCryptAlg, "aes128");
1875 m_strreplace(&SmimeCryptAlg, "aes192");
1878 m_strreplace(&SmimeCryptAlg, "aes256");
1883 case 4: /* (c)lear */
1884 p_delete(&SmimeCryptAlg);
1886 case -1: /* Ctrl-G or Enter */
1890 } while (choice == -1);
1894 case 2: /* (s)ign */
1896 if (!SmimeDefaultKey)
1897 mutt_message (_("Can't sign: No key specified. Use Sign As."));
1900 msg->security |= SIGN;
1901 msg->security &= ~ENCRYPT;
1905 case 4: /* sign (a)s */
1907 if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1908 m_strreplace(&SmimeDefaultKey, p);
1910 msg->security |= SIGN;
1912 /* probably need a different passphrase */
1913 crypt_smime_void_passphrase ();
1917 msg->security &= ~SIGN;
1920 *redraw = REDRAW_FULL;
1923 case 5: /* (b)oth */
1924 msg->security |= (ENCRYPT | SIGN);
1927 case 6: /* (f)orget it */
1928 case 7: /* (c)lear */
1933 if (msg->security && msg->security != APPLICATION_SMIME)
1934 msg->security |= APPLICATION_SMIME;
1938 return (msg->security);