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>
20 #include <lib-ui/curses.h>
21 #include <lib-ui/enter.h>
22 #include <lib-ui/menu.h>
31 struct smime_command_context {
32 const char *key; /* %k */
33 const char *cryptalg; /* %a */
34 const char *fname; /* %f */
35 const char *sig_fname; /* %s */
36 const char *certificates; /* %c */
37 const char *intermediates; /* %i */
46 char trust; /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
47 short public; /* 1=public 0=private */
51 char SmimePass[STRING];
52 time_t SmimeExptime = 0; /* when does the cached passphrase expire? */
55 static char SmimeKeyToUse[_POSIX_PATH_MAX] = { 0 };
56 static char SmimeCertToUse[_POSIX_PATH_MAX];
57 static char SmimeIntermediateToUse[_POSIX_PATH_MAX];
61 * Create a format string to be used with scanf.
62 * To use it, write, for instance, MUTT_FORMAT(HUGE_STRING).
64 * See K&R 2nd ed, p. 231 for an explanation.
66 #define _MUTT_FORMAT_2(a,b) "%" a b
67 #define _MUTT_FORMAT_1(a, b) _MUTT_FORMAT_2(#a, b)
68 #define MUTT_FORMAT(a) _MUTT_FORMAT_1(a, "s")
72 * Queries and passphrase handling.
76 /* these are copies from pgp.c */
79 void smime_void_passphrase (void)
81 p_clear(SmimePass, countof(SmimePass));
85 int smime_valid_passphrase (void)
87 time_t now = time (NULL);
89 if (now < SmimeExptime)
90 /* Use cached copy. */
93 smime_void_passphrase ();
95 if (mutt_get_field_unbuffered (_("Enter S/MIME passphrase:"), SmimePass,
96 sizeof (SmimePass), M_PASS) == 0) {
97 SmimeExptime = time (NULL) + SmimeTimeout;
108 * The OpenSSL interface
111 /* This is almost identical to ppgp's invoking interface. */
114 _mutt_fmt_smime_command (char *dest, ssize_t destlen, char op,
115 const char *src, const char *prefix,
116 const char *ifstring, const char *elsestring,
117 unsigned long data, format_flag flags)
120 struct smime_command_context *cctx = (struct smime_command_context *) data;
121 int optional = (flags & M_FORMAT_OPTIONAL);
127 char path[_POSIX_PATH_MAX];
128 char buf1[LONG_STRING], buf2[LONG_STRING];
131 m_strcpy(path, sizeof(path), NONULL(SmimeCALocation));
132 mutt_expand_path (path, sizeof (path));
133 mutt_quote_filename (buf1, sizeof (buf1), path);
135 if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
136 snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
138 snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
140 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
141 snprintf (dest, destlen, fmt, buf2);
143 else if (!SmimeCALocation)
149 { /* certificate (list) */
151 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
152 snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
154 else if (!cctx->certificates)
160 { /* intermediate certificates */
162 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
163 snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
165 else if (!cctx->intermediates)
171 { /* detached signature */
173 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
174 snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
176 else if (!cctx->sig_fname)
184 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
185 snprintf (dest, destlen, fmt, NONULL (cctx->key));
193 { /* algorithm for encryption */
195 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
196 snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
204 { /* file to process */
206 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
207 snprintf (dest, destlen, fmt, NONULL (cctx->fname));
209 else if (!cctx->fname)
220 mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
222 else if (flags & M_FORMAT_OPTIONAL)
223 mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
231 static void mutt_smime_command (char *d, ssize_t dlen,
232 struct smime_command_context *cctx,
235 mutt_FormatString (d, dlen, NONULL (fmt), _mutt_fmt_smime_command,
236 (unsigned long) cctx, 0);
239 static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
240 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
241 int smimeerrfd, const char *fname,
242 const char *sig_fname, const char *cryptalg,
243 const char *key, const char *certificates,
244 const char *intermediates, const char *format)
246 struct smime_command_context cctx;
247 char cmd[HUGE_STRING];
251 if (!format || !*format)
255 cctx.sig_fname = sig_fname;
257 cctx.cryptalg = cryptalg;
258 cctx.certificates = certificates;
259 cctx.intermediates = intermediates;
261 mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
263 return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
264 smimeinfd, smimeoutfd, smimeerrfd);
273 * Key and certificate handling.
279 Search the certificate index for given mailbox.
280 return certificate file name.
283 static void smime_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
285 smime_id *Table = (smime_id *) menu->data;
286 smime_id this = Table[num];
287 const char *truststate;
289 switch (this.trust) {
291 truststate = N_("Trusted ");
294 truststate = N_("Verified ");
297 truststate = N_("Unverified");
300 truststate = N_("Expired ");
303 truststate = N_("Revoked ");
306 truststate = N_("Invalid ");
309 truststate = N_("Unknown ");
312 snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
313 truststate, this.email, this.nick);
315 snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
316 this.email, this.nick);
323 char *smime_ask_for_key (char *prompt, char *mailbox __attribute__((unused)),
328 long cert_num; /* Will contain the number of certificates.
329 * To be able to get it, the .index file will be read twice... */
330 char index_file[_POSIX_PATH_MAX];
332 char buf[LONG_STRING];
333 char fields[5][STRING];
334 int numFields, hash_suffix, done, cur; /* The current entry */
337 char helpstr[HUGE_STRING * 3];
342 prompt = _("Enter keyID: ");
343 snprintf (index_file, sizeof (index_file), "%s/.index",
344 public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));
346 idx = fopen (index_file, "r");
348 mutt_perror (index_file);
353 while (!feof (idx)) {
354 if (fgets (buf, sizeof (buf), idx))
361 if (mutt_get_field (prompt, qry, sizeof (qry), 0))
363 snprintf (title, sizeof (title),
364 _("S/MIME certificates matching \"%s\"."), qry);
367 idx = fopen (index_file, "r");
369 mutt_perror (index_file);
374 Table = p_new(smime_id, cert_num);
375 while (!feof (idx)) {
377 fscanf (idx, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
378 fields[0], &hash, &hash_suffix, fields[2]);
380 fscanf (idx, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
381 fields[3], fields[4]);
383 /* 0=email 1=name 2=nick 3=intermediate 4=trust */
387 /* Check if query matches this certificate */
388 if (!m_stristr(fields[0], qry) && !m_stristr(fields[2], qry))
391 Table[cur].hash = hash;
392 Table[cur].suffix = hash_suffix;
393 m_strcpy(Table[cur].email, sizeof(Table[cur].email), fields[0]);
394 m_strcpy(Table[cur].nick, sizeof(Table[cur].nick), fields[2]);
395 Table[cur].trust = *fields[4];
396 Table[cur].public = public;
402 /* Make Helpstring */
404 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT);
405 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
406 mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME,
407 OP_GENERIC_SELECT_ENTRY);
408 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
409 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
410 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
412 /* Create the menu */
413 menu = mutt_new_menu ();
415 menu->make_entry = smime_entry;
416 menu->menu = MENU_SMIME;
417 menu->help = helpstr;
420 /* sorting keys might be done later - TODO */
427 switch (mutt_menuLoop (menu)) {
428 case OP_GENERIC_SELECT_ENTRY:
440 fname = p_new(char, 13); /* Hash + '.' + Suffix + \0 */
441 sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
446 mutt_menuDestroy (&menu);
448 set_option (OPTNEEDREDRAW);
457 char *smime_get_field_from_db (char *mailbox, char *query, short public,
460 int addr_len, query_len, found = 0, ask = 0, choice = 0;
461 char cert_path[_POSIX_PATH_MAX];
462 char buf[LONG_STRING], prompt[STRING];
463 char fields[5][STRING];
467 char key_trust_level = 0;
470 if (!mailbox && !query)
473 addr_len = mailbox ? m_strlen(mailbox) : 0;
474 query_len = query ? m_strlen(query) : 0;
478 /* index-file format:
479 mailbox certfile label issuer_certfile trust_flags\n
481 certfile is a hash value generated by openssl.
482 Note that this was done according to the OpenSSL
483 specs on their CA-directory.
486 snprintf (cert_path, sizeof (cert_path), "%s/.index",
487 (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));
489 if (!stat (cert_path, &info)) {
490 if ((fp = safe_fopen (cert_path, "r")) == NULL) {
491 mutt_perror (cert_path);
495 while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
496 if (mailbox && !(m_strncasecmp(mailbox, buf, addr_len))) {
497 numFields = sscanf (buf,
498 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
499 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
500 MUTT_FORMAT (STRING) "\n",
501 fields[0], fields[1],
502 fields[2], fields[3], fields[4]);
505 if (mailbox && public &&
507 *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
511 if (public && *fields[4] == 'u')
512 snprintf (prompt, sizeof (prompt),
514 ("ID %s is unverified. Do you want to use it for %s ?"),
516 else if (public && *fields[4] == 'v')
517 snprintf (prompt, sizeof (prompt),
518 _("Use (untrusted!) ID %s for %s ?"),
521 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
525 if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
531 else if (choice == M_NO) {
535 else if (choice == M_YES) {
536 m_strcpy(key, sizeof(key), fields[1]);
543 key_trust_level = *fields[4];
544 m_strcpy(key, sizeof(key), fields[1]);
549 numFields = sscanf (buf,
550 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
551 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
552 MUTT_FORMAT (STRING) "\n",
553 fields[0], fields[1],
554 fields[2], fields[3], fields[4]);
556 /* query = label: return certificate. */
557 if (numFields >= 3 &&
558 !(m_strncasecmp(query, fields[2], query_len))) {
560 m_strcpy(key, sizeof(key), fields[1]);
562 /* query = certificate: return intermediate certificate. */
563 else if (numFields >= 4 &&
564 !(m_strncasecmp(query, fields[1], query_len))) {
566 m_strcpy(key, sizeof(key), fields[3]);
573 if (public && *fields[4] == 'u')
574 snprintf (prompt, sizeof (prompt),
575 _("ID %s is unverified. Do you want to use it for %s ?"),
577 else if (public && *fields[4] == 'v')
578 snprintf (prompt, sizeof (prompt),
579 _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
581 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
583 choice = mutt_yesorno (prompt, M_NO);
584 if (choice == -1 || choice == M_NO)
587 else if (key_trust_level && may_ask) {
588 if (key_trust_level == 'u') {
589 snprintf (prompt, sizeof (prompt),
590 _("ID %s is unverified. Do you want to use it for %s ?"),
592 choice = mutt_yesorno (prompt, M_NO);
596 else if (key_trust_level == 'v') {
598 ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
606 /* Note: m_strdup("") returns NULL. */
607 return m_strdup(key);
614 This sets the '*ToUse' variables for an upcoming decryption, where
615 the reuquired key is different from SmimeDefaultKey.
618 void _smime_getkeys (char *mailbox)
623 k = smime_get_field_from_db (mailbox, NULL, 0, 1);
626 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
627 k = smime_ask_for_key (buf, mailbox, 0);
631 /* the key used last time. */
632 if (*SmimeKeyToUse &&
633 !m_strcasecmp(k, SmimeKeyToUse + m_strlen(SmimeKeys) + 1)) {
638 smime_void_passphrase ();
640 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
641 NONULL (SmimeKeys), k);
643 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
644 NONULL (SmimeCertificates), k);
646 if (m_strcasecmp(k, SmimeDefaultKey))
647 smime_void_passphrase ();
653 if (*SmimeKeyToUse) {
654 if (!m_strcasecmp(SmimeDefaultKey,
655 SmimeKeyToUse + m_strlen(SmimeKeys) + 1))
658 smime_void_passphrase ();
661 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
662 NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
664 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
665 NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
668 void smime_getkeys (ENVELOPE * env)
673 if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
674 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
675 NONULL (SmimeKeys), SmimeDefaultKey);
677 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
678 NONULL (SmimeCertificates), SmimeDefaultKey);
683 for (t = env->to; !found && t; t = t->next)
684 if (mutt_addr_is_user (t)) {
686 _smime_getkeys (t->mailbox);
688 for (t = env->cc; !found && t; t = t->next)
689 if (mutt_addr_is_user (t)) {
691 _smime_getkeys (t->mailbox);
693 if (!found && (t = mutt_default_from ())) {
694 _smime_getkeys (t->mailbox);
695 address_list_wipe(&t);
699 /* This routine attempts to find the keyids of the recipients of a message.
700 * It returns NULL if any of the keys can not be found.
703 char *smime_findKeys (address_t * to, address_t * cc, address_t * bcc)
705 char *keyID, *keylist = NULL;
706 ssize_t keylist_size = 0;
707 ssize_t keylist_used = 0;
708 address_t *tmp = NULL, *addr = NULL;
709 address_t **last = &tmp;
713 const char *fqdn = mutt_fqdn (1);
715 for (i = 0; i < 3; i++) {
730 *last = address_list_dup (p);
732 last = &((*last)->next);
736 rfc822_qualify (tmp, fqdn);
738 address_list_uniq(&tmp);
740 for (p = tmp; p; p = p->next) {
741 char buf[LONG_STRING];
745 if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
746 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
747 keyID = smime_ask_for_key (buf, q->mailbox, 1);
750 mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
752 address_list_wipe(&tmp);
753 address_list_wipe(&addr);
757 keylist_size += m_strlen(keyID) + 2;
758 p_realloc(&keylist, keylist_size);
759 sprintf (keylist + keylist_used, "%s\n", keyID); /* __SPRINTF_CHECKED__ */
760 keylist_used = m_strlen(keylist);
762 address_list_wipe(&addr);
765 address_list_wipe(&tmp);
774 static int smime_handle_cert_email (char *certificate, char *mailbox,
775 int copy, char ***buffer, int *num)
777 FILE *fpout = NULL, *fperr = NULL;
778 char tmpfname[_POSIX_PATH_MAX];
780 int ret = -1, count = 0;
783 mutt_mktemp (tmpfname);
784 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
785 mutt_perror (tmpfname);
788 mutt_unlink (tmpfname);
790 mutt_mktemp (tmpfname);
791 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
793 mutt_perror (tmpfname);
796 mutt_unlink (tmpfname);
798 if ((thepid = smime_invoke (NULL, NULL, NULL,
799 -1, fileno (fpout), fileno (fperr),
800 certificate, NULL, NULL, NULL, NULL, NULL,
801 SmimeGetCertEmailCommand)) == -1) {
802 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
808 mutt_wait_filter (thepid);
816 while ((fgets (email, sizeof (email), fpout))) {
817 *(email + m_strlen(email) - 1) = '\0';
818 if (m_strncasecmp(email, mailbox, m_strlen(mailbox)) == 0)
821 ret = ret < 0 ? 0 : ret;
827 mutt_copy_stream (fperr, stdout);
828 mutt_any_key_to_continue (_
829 ("Error: unable to create OpenSSL subprocess!"));
837 if (copy && buffer && num) {
839 *buffer = p_new(char *, count);
843 while ((fgets (email, sizeof (email), fpout))) {
844 *(email + m_strlen(email) - 1) = '\0';
845 (*buffer)[count] = p_dupstr(email, m_strlen(email));
860 static char *smime_extract_certificate (char *infile)
862 FILE *fpout = NULL, *fperr = NULL;
863 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
864 char tmpfname[_POSIX_PATH_MAX];
869 mutt_mktemp (tmpfname);
870 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
871 mutt_perror (tmpfname);
874 mutt_unlink (tmpfname);
876 mutt_mktemp (pk7out);
877 if ((fpout = safe_fopen (pk7out, "w+")) == NULL) {
879 mutt_perror (pk7out);
883 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
884 extract the full set of certificates directly.
886 if ((thepid = smime_invoke (NULL, NULL, NULL,
887 -1, fileno (fpout), fileno (fperr),
888 infile, NULL, NULL, NULL, NULL, NULL,
889 SmimePk7outCommand)) == -1) {
890 mutt_any_key_to_continue (_
891 ("Error: unable to create OpenSSL subprocess!"));
894 mutt_unlink (pk7out);
898 mutt_wait_filter (thepid);
905 empty = (fgetc (fpout) == EOF);
907 mutt_perror (pk7out);
908 mutt_copy_stream (fperr, stdout);
911 mutt_unlink (pk7out);
918 mutt_mktemp (certfile);
919 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
921 mutt_unlink (pk7out);
922 mutt_perror (certfile);
926 /* Step 2: Extract the certificates from a PKCS#7 structure.
928 if ((thepid = smime_invoke (NULL, NULL, NULL,
929 -1, fileno (fpout), fileno (fperr),
930 pk7out, NULL, NULL, NULL, NULL, NULL,
931 SmimeGetCertCommand)) == -1) {
932 mutt_any_key_to_continue (_
933 ("Error: unable to create OpenSSL subprocess!"));
936 mutt_unlink (pk7out);
937 mutt_unlink (certfile);
941 mutt_wait_filter (thepid);
943 mutt_unlink (pk7out);
949 empty = (fgetc (fpout) == EOF);
951 mutt_copy_stream (fperr, stdout);
954 mutt_unlink (certfile);
961 return m_strdup(certfile);
964 static char *smime_extract_signer_certificate (char *infile)
966 FILE *fpout = NULL, *fperr = NULL;
967 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
968 char tmpfname[_POSIX_PATH_MAX];
973 mutt_mktemp (tmpfname);
974 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
975 mutt_perror (tmpfname);
978 mutt_unlink (tmpfname);
981 mutt_mktemp (certfile);
982 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
984 mutt_perror (certfile);
988 /* Extract signer's certificate
990 if ((thepid = smime_invoke (NULL, NULL, NULL,
991 -1, -1, fileno (fperr),
992 infile, NULL, NULL, NULL, certfile, NULL,
993 SmimeGetSignerCertCommand)) == -1) {
994 mutt_any_key_to_continue (_
995 ("Error: unable to create OpenSSL subprocess!"));
998 mutt_unlink (pk7out);
999 mutt_unlink (certfile);
1003 mutt_wait_filter (thepid);
1009 empty = (fgetc (fpout) == EOF);
1012 mutt_copy_stream (fperr, stdout);
1013 mutt_any_key_to_continue (NULL);
1016 mutt_unlink (certfile);
1023 return m_strdup(certfile);
1029 /* Add a certificate and update index file (externally). */
1031 void smime_invoke_import (char *infile, char *mailbox __attribute__ ((unused)))
1033 char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1034 FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1037 mutt_mktemp (tmpfname);
1038 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1039 mutt_perror (tmpfname);
1042 mutt_unlink (tmpfname);
1044 mutt_mktemp (tmpfname);
1045 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
1047 mutt_perror (tmpfname);
1050 mutt_unlink (tmpfname);
1054 if (option (OPTASKCERTLABEL))
1055 mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1058 if ((certfile = smime_extract_certificate (infile))) {
1061 if ((thepid = smime_invoke (&smimein, NULL, NULL,
1062 -1, fileno (fpout), fileno (fperr),
1063 certfile, NULL, NULL, NULL, NULL, NULL,
1064 SmimeImportCertCommand)) == -1) {
1065 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1068 fputs (buf, smimein);
1069 fputc ('\n', smimein);
1072 mutt_wait_filter (thepid);
1074 mutt_unlink (certfile);
1075 p_delete(&certfile);
1083 mutt_copy_stream (fpout, stdout);
1084 mutt_copy_stream (fperr, stdout);
1093 int smime_verify_sender (HEADER * h)
1095 char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1099 mutt_mktemp (tempfname);
1100 if (!(fpout = safe_fopen (tempfname, "w"))) {
1101 mutt_perror (tempfname);
1105 if (h->security & ENCRYPT)
1106 mutt_copy_message (fpout, Context, h,
1107 M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1108 CH_MIME | CH_WEED | CH_NONEWLINE);
1110 mutt_copy_message (fpout, Context, h, 0, 0);
1116 h->env->from = mutt_expand_aliases (h->env->from);
1117 mbox = h->env->from->mailbox;
1119 else if (h->env->sender) {
1120 h->env->sender = mutt_expand_aliases (h->env->sender);
1121 mbox = h->env->sender->mailbox;
1125 if ((certfile = smime_extract_signer_certificate (tempfname))) {
1126 mutt_unlink (tempfname);
1127 if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1129 mutt_any_key_to_continue (NULL);
1133 mutt_unlink (certfile);
1134 p_delete(&certfile);
1137 mutt_any_key_to_continue (_("no certfile"));
1140 mutt_any_key_to_continue (_("no mbox"));
1142 mutt_unlink (tempfname);
1155 * Creating S/MIME - bodies.
1162 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1163 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1164 int smimeerrfd, const char *fname,
1167 return smime_invoke (smimein, smimeout, smimeerr,
1168 smimeinfd, smimeoutfd, smimeerrfd,
1169 fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1170 SmimeEncryptCommand);
1175 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1176 int smimeinfd, int smimeoutfd, int smimeerrfd,
1179 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1180 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1181 SmimeCertToUse, SmimeIntermediateToUse,
1188 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1190 char buf[LONG_STRING], certfile[LONG_STRING];
1191 char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1192 char smimeinfile[_POSIX_PATH_MAX];
1193 char *cert_start = certlist, *cert_end = certlist;
1194 FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1199 mutt_mktemp (tempfile);
1200 if ((fpout = safe_fopen (tempfile, "w+")) == NULL) {
1201 mutt_perror (tempfile);
1205 mutt_mktemp (smimeerrfile);
1206 if ((smimeerr = safe_fopen (smimeerrfile, "w+")) == NULL) {
1207 mutt_perror (smimeerrfile);
1209 mutt_unlink (tempfile);
1212 mutt_unlink (smimeerrfile);
1214 mutt_mktemp (smimeinfile);
1215 if ((fptmp = safe_fopen (smimeinfile, "w+")) == NULL) {
1216 mutt_perror (smimeinfile);
1217 mutt_unlink (tempfile);
1225 int off = m_strlen(certfile);
1227 while (*++cert_end && *cert_end != '\n');
1231 snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1232 NONULL (SmimeCertificates), cert_start);
1234 cert_start = cert_end;
1238 /* write a MIME entity */
1239 mutt_write_mime_header (a, fptmp);
1240 fputc ('\n', fptmp);
1241 mutt_write_mime_body (a, fptmp);
1245 smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1246 fileno (fpout), fileno (smimeerr),
1247 smimeinfile, certfile)) == -1) {
1249 mutt_unlink (smimeinfile);
1250 mutt_unlink (certfile);
1256 mutt_wait_filter (thepid);
1257 mutt_unlink (smimeinfile);
1258 mutt_unlink (certfile);
1262 empty = (fgetc (fpout) == EOF);
1267 while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1269 fputs (buf, stdout);
1273 /* pause if there is any error output from SMIME */
1275 mutt_any_key_to_continue (NULL);
1278 /* fatal error while trying to encrypt message */
1280 mutt_any_key_to_continue _("No output from OpenSSL..");
1282 mutt_unlink (tempfile);
1287 t->type = TYPEAPPLICATION;
1288 t->subtype = m_strdup("x-pkcs7-mime");
1289 parameter_setval(&t->parameter, "name", "smime.p7m");
1290 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
1291 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
1293 t->disposition = DISPATTACH;
1294 t->d_filename = m_strdup("smime.p7m");
1295 t->filename = m_strdup(tempfile);
1296 t->unlink = 1; /*delete after sending the message */
1306 BODY *smime_sign_message (BODY * a)
1309 char buffer[LONG_STRING];
1310 char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1311 FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1315 char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1317 if (!intermediates) {
1318 mutt_message (_("Warning: Intermediate certificate not found."));
1319 intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
1322 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
1324 mutt_mktemp (filetosign);
1325 if ((sfp = safe_fopen (filetosign, "w+")) == NULL) {
1326 mutt_perror (filetosign);
1330 mutt_mktemp (signedfile);
1331 if ((smimeout = safe_fopen (signedfile, "w+")) == NULL) {
1332 mutt_perror (signedfile);
1334 mutt_unlink (filetosign);
1338 mutt_write_mime_header (a, sfp);
1340 mutt_write_mime_body (a, sfp);
1345 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1346 NONULL (SmimeKeys), SmimeDefaultKey);
1348 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1349 NONULL (SmimeCertificates), SmimeDefaultKey);
1351 snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1352 NONULL (SmimeCertificates), intermediates);
1356 if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1357 -1, fileno (smimeout), -1,
1358 filetosign)) == -1) {
1359 mutt_perror (_("Can't open OpenSSL subprocess!"));
1362 mutt_unlink (signedfile);
1363 mutt_unlink (filetosign);
1366 fputs (SmimePass, smimein);
1367 fputc ('\n', smimein);
1371 mutt_wait_filter (thepid);
1373 /* check for errors from OpenSSL */
1377 while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1379 fputs (buffer, stdout);
1386 empty = (fgetc (smimeout) == EOF);
1389 mutt_unlink (filetosign);
1393 mutt_any_key_to_continue (NULL);
1396 mutt_any_key_to_continue _("No output from OpenSSL...");
1398 mutt_unlink (signedfile);
1399 return (NULL); /* fatal error while signing */
1403 t->type = TYPEMULTIPART;
1404 t->subtype = m_strdup("signed");
1405 t->encoding = ENC7BIT;
1407 t->disposition = DISPINLINE;
1409 parameter_set_boundary(&t->parameter);
1410 /* check if this can be extracted from private key somehow.... */
1411 parameter_setval(&t->parameter, "micalg", "sha1");
1412 parameter_setval(&t->parameter, "protocol",
1413 "application/x-pkcs7-signature");
1418 t->parts->next = body_new();
1420 t->type = TYPEAPPLICATION;
1421 t->subtype = m_strdup("x-pkcs7-signature");
1422 t->filename = m_strdup(signedfile);
1423 t->d_filename = m_strdup("smime.p7s");
1425 t->disposition = DISPATTACH;
1426 t->encoding = ENCBASE64;
1427 t->unlink = 1; /* ok to remove this file after sending. */
1435 * Handling S/MIME - bodies.
1440 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1441 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1442 int smimeerrfd, const char *fname,
1443 const char *sig_fname, int opaque)
1445 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1446 smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1447 (opaque ? SmimeVerifyOpaqueCommand :
1448 SmimeVerifyCommand));
1453 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1454 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1455 int smimeerrfd, const char *fname)
1457 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1458 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1459 SmimeCertToUse, NULL, SmimeDecryptCommand);
1464 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1466 char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1467 FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1472 ssize_t tmplength = 0;
1473 int origType = sigbdy->type;
1474 char *savePrefix = NULL;
1477 snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1479 /* decode to a tempfile, saving the original destination */
1481 if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1482 mutt_perror (signedfile);
1485 /* decoding the attachment changes the size and offset, so save a copy
1486 * of the "real" values now, and restore them after processing
1488 tmplength = sigbdy->length;
1489 tmpoffset = sigbdy->offset;
1491 /* if we are decoding binary bodies, we don't want to prefix each
1492 * line with the prefix or else the data will get corrupted.
1494 savePrefix = s->prefix;
1497 mutt_decode_attachment (sigbdy, s);
1499 sigbdy->length = ftello (s->fpout);
1503 /* restore final destination and substitute the tempfile for input */
1506 s->fpin = fopen (signedfile, "r");
1508 /* restore the prefix */
1509 s->prefix = savePrefix;
1511 sigbdy->type = origType;
1514 mutt_mktemp (smimeerrfile);
1515 if (!(smimeerr = safe_fopen (smimeerrfile, "w+"))) {
1516 mutt_perror (smimeerrfile);
1517 mutt_unlink (signedfile);
1521 crypt_current_time (s, "OpenSSL");
1523 if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1524 -1, -1, fileno (smimeerr),
1525 tempfile, signedfile, 0)) != -1) {
1529 if (mutt_wait_filter (thepid))
1539 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1540 if (linelen && !m_strcasecmp(line, "verification successful"))
1549 mutt_copy_stream (smimeerr, s->fpout);
1552 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1554 mutt_unlink (signedfile);
1555 mutt_unlink (smimeerrfile);
1557 sigbdy->length = tmplength;
1558 sigbdy->offset = tmpoffset;
1560 /* restore the original source stream */
1573 This handles application/pkcs7-mime which can either be a signed
1574 or an encrypted message.
1577 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1582 char buf[HUGE_STRING];
1583 char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1584 char tmpfname[_POSIX_PATH_MAX];
1585 char tmptmpfname[_POSIX_PATH_MAX];
1586 FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1587 FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1591 unsigned int type = mutt_is_application_smime (m);
1593 if (!(type & APPLICATION_SMIME))
1596 mutt_mktemp (outfile);
1597 if ((smimeout = safe_fopen (outfile, "w+")) == NULL) {
1598 mutt_perror (outfile);
1602 mutt_mktemp (errfile);
1603 if ((smimeerr = safe_fopen (errfile, "w+")) == NULL) {
1604 mutt_perror (errfile);
1609 mutt_unlink (errfile);
1612 mutt_mktemp (tmpfname);
1613 if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL) {
1614 mutt_perror (tmpfname);
1622 fseeko (s->fpin, m->offset, 0);
1623 last_pos = m->offset;
1625 mutt_copy_bytes (s->fpin, tmpfp, m->length);
1630 if ((type & ENCRYPT) &&
1631 (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1632 fileno (smimeout), fileno (smimeerr),
1636 mutt_unlink (tmpfname);
1637 if (s->flags & M_DISPLAY)
1638 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1641 else if ((type & SIGNOPAQUE) &&
1642 (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1644 fileno (smimeerr), NULL, tmpfname,
1645 SIGNOPAQUE)) == -1) {
1648 mutt_unlink (tmpfname);
1649 if (s->flags & M_DISPLAY)
1650 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1655 if (type & ENCRYPT) {
1656 if (!smime_valid_passphrase ())
1657 smime_void_passphrase ();
1658 fputs (SmimePass, smimein);
1659 fputc ('\n', smimein);
1664 mutt_wait_filter (thepid);
1665 mutt_unlink (tmpfname);
1668 if (s->flags & M_DISPLAY) {
1671 if ((c = fgetc (smimeerr)) != EOF) {
1672 ungetc (c, smimeerr);
1674 crypt_current_time (s, "OpenSSL");
1675 mutt_copy_stream (smimeerr, s->fpout);
1676 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1680 state_attach_puts (_("[-- The following data is S/MIME"
1681 " encrypted --]\n"), s);
1683 state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1694 mutt_mktemp (tmptmpfname);
1695 if ((fpout = safe_fopen (tmptmpfname, "w+")) == NULL) {
1696 mutt_perror (tmptmpfname);
1702 while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1703 len = m_strlen(buf);
1704 if (len > 1 && buf[len - 2] == '\r') {
1705 buf[len - 2] = '\n';
1706 buf[len - 1] = '\0';
1714 if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1715 fstat (fileno (fpout), &info);
1716 p->length = info.st_size - p->offset;
1718 mutt_parse_part (fpout, p);
1721 tmpfp_buffer = s->fpin;
1723 mutt_body_handler (p, s);
1724 s->fpin = tmpfp_buffer;
1730 mutt_unlink (outfile);
1734 mutt_unlink (tmptmpfname);
1739 if (s->flags & M_DISPLAY) {
1741 state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1743 state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1746 if (type & SIGNOPAQUE) {
1753 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1754 if (linelen && !m_strcasecmp(line, "verification successful"))
1759 m->goodsig = p->goodsig;
1760 m->badsig = p->badsig;
1771 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1775 char tempfile[_POSIX_PATH_MAX];
1777 long tmpoffset = b->offset;
1778 ssize_t tmplength = b->length;
1779 int origType = b->type;
1783 if (!mutt_is_application_smime (b))
1791 fseeko (s.fpin, b->offset, 0);
1793 mutt_mktemp (tempfile);
1794 if ((tmpfp = safe_fopen (tempfile, "w+")) == NULL) {
1795 mutt_perror (tempfile);
1799 mutt_unlink (tempfile);
1801 mutt_decode_attachment (b, &s);
1803 b->length = ftello (s.fpout);
1809 mutt_mktemp (tempfile);
1810 if ((*fpout = safe_fopen (tempfile, "w+")) == NULL) {
1811 mutt_perror (tempfile);
1815 mutt_unlink (tempfile);
1817 if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1822 (*cur)->goodsig = b->goodsig;
1823 (*cur)->badsig = b->badsig;
1827 b->length = tmplength;
1828 b->offset = tmpoffset;
1830 safe_fclose (&tmpfp);
1837 int smime_application_smime_handler (BODY * m, STATE * s)
1839 return smime_handle_entity (m, s, NULL) ? 0 : -1;
1842 int smime_send_menu (HEADER * msg, int *redraw)
1846 switch (mutt_multi_choice
1847 (_("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1849 case 1: /* (e)ncrypt */
1850 msg->security |= ENCRYPT;
1851 msg->security &= ~SIGN;
1854 case 3: /* encrypt (w)ith */
1857 msg->security |= ENCRYPT;
1860 /* I use "dra" because "123" is recognized anyway */
1861 switch (mutt_multi_choice (_("Choose algorithm family:"
1862 " 1: DES, 2: RC2, 3: AES,"
1863 " or (c)lear? "), _("drac"))) {
1865 switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
1868 m_strreplace(&SmimeCryptAlg, "des");
1871 m_strreplace(&SmimeCryptAlg, "des3");
1877 switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
1880 m_strreplace(&SmimeCryptAlg, "rc2-40");
1883 m_strreplace(&SmimeCryptAlg, "rc2-64");
1886 m_strreplace(&SmimeCryptAlg, "rc2-128");
1892 switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
1895 m_strreplace(&SmimeCryptAlg, "aes128");
1898 m_strreplace(&SmimeCryptAlg, "aes192");
1901 m_strreplace(&SmimeCryptAlg, "aes256");
1906 case 4: /* (c)lear */
1907 p_delete(&SmimeCryptAlg);
1909 case -1: /* Ctrl-G or Enter */
1913 } while (choice == -1);
1917 case 2: /* (s)ign */
1919 if (!SmimeDefaultKey)
1920 mutt_message (_("Can't sign: No key specified. Use Sign As."));
1923 msg->security |= SIGN;
1924 msg->security &= ~ENCRYPT;
1928 case 4: /* sign (a)s */
1930 if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1931 m_strreplace(&SmimeDefaultKey, p);
1933 msg->security |= SIGN;
1935 /* probably need a different passphrase */
1936 crypt_smime_void_passphrase ();
1940 msg->security &= ~SIGN;
1943 *redraw = REDRAW_FULL;
1946 case 5: /* (b)oth */
1947 msg->security |= (ENCRYPT | SIGN);
1950 case 6: /* (f)orget it */
1951 case 7: /* (c)lear */
1956 if (msg->security && msg->security != APPLICATION_SMIME)
1957 msg->security |= APPLICATION_SMIME;
1961 return (msg->security);