2 * Copyright notice from original mutt:
3 * Copyright (C) 2001,2002 Oliver Ehli <elmy@acm.org>
4 * Copyright (C) 2002 Mike Schiraldi <raldi@research.netsol.com>
5 * Copyright (C) 2004 g10 Code GmbH
7 * This file is part of mutt-ng, see http://www.muttng.org/.
8 * It's licensed under the GNU General Public License,
9 * please see the file GPL in the top level source directory.
16 #include <lib-lib/mem.h>
17 #include <lib-lib/macros.h>
22 #include "mutt_curses.h"
23 #include "mutt_menu.h"
29 #include "lib/debug.h"
43 #ifdef HAVE_SYS_TIME_H
44 # include <sys/time.h>
47 #ifdef HAVE_SYS_RESOURCE_H
48 # include <sys/resource.h>
51 #ifdef CRYPT_BACKEND_CLASSIC_SMIME
53 #include "mutt_crypt.h"
55 struct smime_command_context {
56 const char *key; /* %k */
57 const char *cryptalg; /* %a */
58 const char *fname; /* %f */
59 const char *sig_fname; /* %s */
60 const char *certificates; /* %c */
61 const char *intermediates; /* %i */
70 char trust; /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
71 short public; /* 1=public 0=private */
75 char SmimePass[STRING];
76 time_t SmimeExptime = 0; /* when does the cached passphrase expire? */
79 static char SmimeKeyToUse[_POSIX_PATH_MAX] = { 0 };
80 static char SmimeCertToUse[_POSIX_PATH_MAX];
81 static char SmimeIntermediateToUse[_POSIX_PATH_MAX];
85 * Queries and passphrase handling.
91 /* these are copies from pgp.c */
94 void smime_void_passphrase (void)
96 memset (SmimePass, 0, sizeof (SmimePass));
100 int smime_valid_passphrase (void)
102 time_t now = time (NULL);
104 if (now < SmimeExptime)
105 /* Use cached copy. */
108 smime_void_passphrase ();
110 if (mutt_get_field_unbuffered (_("Enter S/MIME passphrase:"), SmimePass,
111 sizeof (SmimePass), M_PASS) == 0) {
112 SmimeExptime = time (NULL) + SmimeTimeout;
123 * The OpenSSL interface
126 /* This is almost identical to ppgp's invoking interface. */
128 static const char *_mutt_fmt_smime_command (char *dest,
133 const char *ifstring,
134 const char *elsestring,
139 struct smime_command_context *cctx = (struct smime_command_context *) data;
140 int optional = (flags & M_FORMAT_OPTIONAL);
146 char path[_POSIX_PATH_MAX];
147 char buf1[LONG_STRING], buf2[LONG_STRING];
150 strfcpy (path, NONULL (SmimeCALocation), sizeof (path));
151 mutt_expand_path (path, sizeof (path));
152 mutt_quote_filename (buf1, sizeof (buf1), path);
154 if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
155 snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
157 snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
159 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
160 snprintf (dest, destlen, fmt, buf2);
162 else if (!SmimeCALocation)
168 { /* certificate (list) */
170 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
171 snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
173 else if (!cctx->certificates)
179 { /* intermediate certificates */
181 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
182 snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
184 else if (!cctx->intermediates)
190 { /* detached signature */
192 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
193 snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
195 else if (!cctx->sig_fname)
203 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
204 snprintf (dest, destlen, fmt, NONULL (cctx->key));
212 { /* algorithm for encryption */
214 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
215 snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
223 { /* file to process */
225 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
226 snprintf (dest, destlen, fmt, NONULL (cctx->fname));
228 else if (!cctx->fname)
239 mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
241 else if (flags & M_FORMAT_OPTIONAL)
242 mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
250 static void mutt_smime_command (char *d, size_t dlen,
251 struct smime_command_context *cctx,
254 mutt_FormatString (d, dlen, NONULL (fmt), _mutt_fmt_smime_command,
255 (unsigned long) cctx, 0);
256 debug_print (2, ("%s\n", d));
259 static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
260 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
261 int smimeerrfd, const char *fname,
262 const char *sig_fname, const char *cryptalg,
263 const char *key, const char *certificates,
264 const char *intermediates, const char *format)
266 struct smime_command_context cctx;
267 char cmd[HUGE_STRING];
269 memset (&cctx, 0, sizeof (cctx));
271 if (!format || !*format)
275 cctx.sig_fname = sig_fname;
277 cctx.cryptalg = cryptalg;
278 cctx.certificates = certificates;
279 cctx.intermediates = intermediates;
281 mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
283 return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
284 smimeinfd, smimeoutfd, smimeerrfd);
293 * Key and certificate handling.
299 Search the certificate index for given mailbox.
300 return certificate file name.
303 static void smime_entry (char *s, size_t l, MUTTMENU * menu, int num)
305 smime_id *Table = (smime_id *) menu->data;
306 smime_id this = Table[num];
307 const char *truststate;
309 switch (this.trust) {
311 truststate = N_("Trusted ");
314 truststate = N_("Verified ");
317 truststate = N_("Unverified");
320 truststate = N_("Expired ");
323 truststate = N_("Revoked ");
326 truststate = N_("Invalid ");
329 truststate = N_("Unknown ");
332 snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
333 truststate, this.email, this.nick);
335 snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
336 this.email, this.nick);
343 char *smime_ask_for_key (char *prompt, char *mailbox, short public)
347 long cert_num; /* Will contain the number of certificates.
348 * To be able to get it, the .index file will be read twice... */
349 char index_file[_POSIX_PATH_MAX];
351 char buf[LONG_STRING];
352 char fields[5][STRING];
353 int numFields, hash_suffix, done, cur; /* The current entry */
356 char helpstr[HUGE_STRING * 3];
361 prompt = _("Enter keyID: ");
362 snprintf (index_file, sizeof (index_file), "%s/.index",
363 public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));
365 index = fopen (index_file, "r");
367 mutt_perror (index_file);
372 while (!feof (index)) {
373 if (fgets (buf, sizeof (buf), index))
380 if (mutt_get_field (prompt, qry, sizeof (qry), 0))
382 snprintf (title, sizeof (title),
383 _("S/MIME certificates matching \"%s\"."), qry);
386 index = fopen (index_file, "r");
388 mutt_perror (index_file);
393 Table = p_new(smime_id, cert_num);
394 while (!feof (index)) {
396 fscanf (index, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
397 fields[0], &hash, &hash_suffix, fields[2]);
399 fscanf (index, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
400 fields[3], fields[4]);
402 /* 0=email 1=name 2=nick 3=intermediate 4=trust */
406 /* Check if query matches this certificate */
407 if (!str_isstr (fields[0], qry) && !str_isstr (fields[2], qry))
410 Table[cur].hash = hash;
411 Table[cur].suffix = hash_suffix;
412 strncpy (Table[cur].email, fields[0], sizeof (Table[cur].email));
413 strncpy (Table[cur].nick, fields[2], sizeof (Table[cur].nick));
414 Table[cur].trust = *fields[4];
415 Table[cur].public = public;
421 /* Make Helpstring */
423 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT);
424 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
425 mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME,
426 OP_GENERIC_SELECT_ENTRY);
427 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
428 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
429 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
431 /* Create the menu */
432 menu = mutt_new_menu ();
434 menu->make_entry = smime_entry;
435 menu->menu = MENU_SMIME;
436 menu->help = helpstr;
439 /* sorting keys might be done later - TODO */
446 switch (mutt_menuLoop (menu)) {
447 case OP_GENERIC_SELECT_ENTRY:
459 fname = p_new(char, 13); /* Hash + '.' + Suffix + \0 */
460 sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
465 mutt_menuDestroy (&menu);
467 set_option (OPTNEEDREDRAW);
476 char *smime_get_field_from_db (char *mailbox, char *query, short public,
479 int addr_len, query_len, found = 0, ask = 0, choice = 0;
480 char cert_path[_POSIX_PATH_MAX];
481 char buf[LONG_STRING], prompt[STRING];
482 char fields[5][STRING];
486 char key_trust_level = 0;
489 if (!mailbox && !query)
492 addr_len = mailbox ? str_len (mailbox) : 0;
493 query_len = query ? str_len (query) : 0;
497 /* index-file format:
498 mailbox certfile label issuer_certfile trust_flags\n
500 certfile is a hash value generated by openssl.
501 Note that this was done according to the OpenSSL
502 specs on their CA-directory.
505 snprintf (cert_path, sizeof (cert_path), "%s/.index",
506 (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));
508 if (!stat (cert_path, &info)) {
509 if ((fp = safe_fopen (cert_path, "r")) == NULL) {
510 mutt_perror (cert_path);
514 while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
515 if (mailbox && !(str_ncasecmp (mailbox, buf, addr_len))) {
516 numFields = sscanf (buf,
517 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
518 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
519 MUTT_FORMAT (STRING) "\n",
520 fields[0], fields[1],
521 fields[2], fields[3], fields[4]);
524 if (mailbox && public &&
526 *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
530 if (public && *fields[4] == 'u')
531 snprintf (prompt, sizeof (prompt),
533 ("ID %s is unverified. Do you want to use it for %s ?"),
535 else if (public && *fields[4] == 'v')
536 snprintf (prompt, sizeof (prompt),
537 _("Use (untrusted!) ID %s for %s ?"),
540 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
544 if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
550 else if (choice == M_NO) {
554 else if (choice == M_YES) {
555 strfcpy (key, fields[1], sizeof (key));
562 key_trust_level = *fields[4];
563 strfcpy (key, fields[1], sizeof (key));
568 numFields = sscanf (buf,
569 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
570 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
571 MUTT_FORMAT (STRING) "\n",
572 fields[0], fields[1],
573 fields[2], fields[3], fields[4]);
575 /* query = label: return certificate. */
576 if (numFields >= 3 &&
577 !(str_ncasecmp (query, fields[2], query_len))) {
579 strfcpy (key, fields[1], sizeof (key));
581 /* query = certificate: return intermediate certificate. */
582 else if (numFields >= 4 &&
583 !(str_ncasecmp (query, fields[1], query_len))) {
585 strfcpy (key, fields[3], sizeof (key));
592 if (public && *fields[4] == 'u')
593 snprintf (prompt, sizeof (prompt),
594 _("ID %s is unverified. Do you want to use it for %s ?"),
596 else if (public && *fields[4] == 'v')
597 snprintf (prompt, sizeof (prompt),
598 _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
600 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
602 choice = mutt_yesorno (prompt, M_NO);
603 if (choice == -1 || choice == M_NO)
606 else if (key_trust_level && may_ask) {
607 if (key_trust_level == 'u') {
608 snprintf (prompt, sizeof (prompt),
609 _("ID %s is unverified. Do you want to use it for %s ?"),
611 choice = mutt_yesorno (prompt, M_NO);
615 else if (key_trust_level == 'v') {
617 ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
625 /* Note: str_dup ("") returns NULL. */
626 return str_dup (key);
633 This sets the '*ToUse' variables for an upcoming decryption, where
634 the reuquired key is different from SmimeDefaultKey.
637 void _smime_getkeys (char *mailbox)
642 k = smime_get_field_from_db (mailbox, NULL, 0, 1);
645 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
646 k = smime_ask_for_key (buf, mailbox, 0);
650 /* the key used last time. */
651 if (*SmimeKeyToUse &&
652 !str_casecmp (k, SmimeKeyToUse + str_len (SmimeKeys) + 1)) {
657 smime_void_passphrase ();
659 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
660 NONULL (SmimeKeys), k);
662 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
663 NONULL (SmimeCertificates), k);
665 if (str_casecmp (k, SmimeDefaultKey))
666 smime_void_passphrase ();
672 if (*SmimeKeyToUse) {
673 if (!str_casecmp (SmimeDefaultKey,
674 SmimeKeyToUse + str_len (SmimeKeys) + 1))
677 smime_void_passphrase ();
680 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
681 NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
683 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
684 NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
687 void smime_getkeys (ENVELOPE * env)
692 if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
693 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
694 NONULL (SmimeKeys), SmimeDefaultKey);
696 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
697 NONULL (SmimeCertificates), SmimeDefaultKey);
702 for (t = env->to; !found && t; t = t->next)
703 if (mutt_addr_is_user (t)) {
705 _smime_getkeys (t->mailbox);
707 for (t = env->cc; !found && t; t = t->next)
708 if (mutt_addr_is_user (t)) {
710 _smime_getkeys (t->mailbox);
712 if (!found && (t = mutt_default_from ())) {
713 _smime_getkeys (t->mailbox);
714 rfc822_free_address (&t);
718 /* This routine attempts to find the keyids of the recipients of a message.
719 * It returns NULL if any of the keys can not be found.
722 char *smime_findKeys (ADDRESS * to, ADDRESS * cc, ADDRESS * bcc)
724 char *keyID, *keylist = NULL;
725 size_t keylist_size = 0;
726 size_t keylist_used = 0;
727 ADDRESS *tmp = NULL, *addr = NULL;
728 ADDRESS **last = &tmp;
732 const char *fqdn = mutt_fqdn (1);
734 for (i = 0; i < 3; i++) {
749 *last = rfc822_cpy_adr (p);
751 last = &((*last)->next);
755 rfc822_qualify (tmp, fqdn);
757 tmp = mutt_remove_duplicates (tmp);
759 for (p = tmp; p; p = p->next) {
760 char buf[LONG_STRING];
764 if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
765 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
766 keyID = smime_ask_for_key (buf, q->mailbox, 1);
769 mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
771 rfc822_free_address (&tmp);
772 rfc822_free_address (&addr);
776 keylist_size += str_len (keyID) + 2;
777 p_realloc(&keylist, keylist_size);
778 sprintf (keylist + keylist_used, "%s\n", keyID); /* __SPRINTF_CHECKED__ */
779 keylist_used = str_len (keylist);
781 rfc822_free_address (&addr);
784 rfc822_free_address (&tmp);
793 static int smime_handle_cert_email (char *certificate, char *mailbox,
794 int copy, char ***buffer, int *num)
796 FILE *fpout = NULL, *fperr = NULL;
797 char tmpfname[_POSIX_PATH_MAX];
799 int ret = -1, count = 0;
802 mutt_mktemp (tmpfname);
803 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
804 mutt_perror (tmpfname);
807 mutt_unlink (tmpfname);
809 mutt_mktemp (tmpfname);
810 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
812 mutt_perror (tmpfname);
815 mutt_unlink (tmpfname);
817 if ((thepid = smime_invoke (NULL, NULL, NULL,
818 -1, fileno (fpout), fileno (fperr),
819 certificate, NULL, NULL, NULL, NULL, NULL,
820 SmimeGetCertEmailCommand)) == -1) {
821 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
827 mutt_wait_filter (thepid);
835 while ((fgets (email, sizeof (email), fpout))) {
836 *(email + str_len (email) - 1) = '\0';
837 if (str_ncasecmp (email, mailbox, str_len (mailbox)) == 0)
840 ret = ret < 0 ? 0 : ret;
846 mutt_copy_stream (fperr, stdout);
847 mutt_any_key_to_continue (_
848 ("Error: unable to create OpenSSL subprocess!"));
856 if (copy && buffer && num) {
858 *buffer = p_new(char *, count);
862 while ((fgets (email, sizeof (email), fpout))) {
863 *(email + str_len (email) - 1) = '\0';
864 (*buffer)[count] = p_dupstr(email, str_len(email));
879 static char *smime_extract_certificate (char *infile)
881 FILE *fpout = NULL, *fperr = NULL;
882 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
883 char tmpfname[_POSIX_PATH_MAX];
888 mutt_mktemp (tmpfname);
889 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
890 mutt_perror (tmpfname);
893 mutt_unlink (tmpfname);
895 mutt_mktemp (pk7out);
896 if ((fpout = safe_fopen (pk7out, "w+")) == NULL) {
898 mutt_perror (pk7out);
902 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
903 extract the full set of certificates directly.
905 if ((thepid = smime_invoke (NULL, NULL, NULL,
906 -1, fileno (fpout), fileno (fperr),
907 infile, NULL, NULL, NULL, NULL, NULL,
908 SmimePk7outCommand)) == -1) {
909 mutt_any_key_to_continue (_
910 ("Error: unable to create OpenSSL subprocess!"));
913 mutt_unlink (pk7out);
917 mutt_wait_filter (thepid);
924 empty = (fgetc (fpout) == EOF);
926 mutt_perror (pk7out);
927 mutt_copy_stream (fperr, stdout);
930 mutt_unlink (pk7out);
937 mutt_mktemp (certfile);
938 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
940 mutt_unlink (pk7out);
941 mutt_perror (certfile);
945 /* Step 2: Extract the certificates from a PKCS#7 structure.
947 if ((thepid = smime_invoke (NULL, NULL, NULL,
948 -1, fileno (fpout), fileno (fperr),
949 pk7out, NULL, NULL, NULL, NULL, NULL,
950 SmimeGetCertCommand)) == -1) {
951 mutt_any_key_to_continue (_
952 ("Error: unable to create OpenSSL subprocess!"));
955 mutt_unlink (pk7out);
956 mutt_unlink (certfile);
960 mutt_wait_filter (thepid);
962 mutt_unlink (pk7out);
968 empty = (fgetc (fpout) == EOF);
970 mutt_copy_stream (fperr, stdout);
973 mutt_unlink (certfile);
980 return str_dup (certfile);
983 static char *smime_extract_signer_certificate (char *infile)
985 FILE *fpout = NULL, *fperr = NULL;
986 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
987 char tmpfname[_POSIX_PATH_MAX];
992 mutt_mktemp (tmpfname);
993 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
994 mutt_perror (tmpfname);
997 mutt_unlink (tmpfname);
1000 mutt_mktemp (certfile);
1001 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
1003 mutt_perror (certfile);
1007 /* Extract signer's certificate
1009 if ((thepid = smime_invoke (NULL, NULL, NULL,
1010 -1, -1, fileno (fperr),
1011 infile, NULL, NULL, NULL, certfile, NULL,
1012 SmimeGetSignerCertCommand)) == -1) {
1013 mutt_any_key_to_continue (_
1014 ("Error: unable to create OpenSSL subprocess!"));
1017 mutt_unlink (pk7out);
1018 mutt_unlink (certfile);
1022 mutt_wait_filter (thepid);
1028 empty = (fgetc (fpout) == EOF);
1031 mutt_copy_stream (fperr, stdout);
1032 mutt_any_key_to_continue (NULL);
1035 mutt_unlink (certfile);
1042 return str_dup (certfile);
1048 /* Add a certificate and update index file (externally). */
1050 void smime_invoke_import (char *infile, char *mailbox)
1052 char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1053 FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1056 mutt_mktemp (tmpfname);
1057 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1058 mutt_perror (tmpfname);
1061 mutt_unlink (tmpfname);
1063 mutt_mktemp (tmpfname);
1064 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
1066 mutt_perror (tmpfname);
1069 mutt_unlink (tmpfname);
1073 if (option (OPTASKCERTLABEL))
1074 mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1077 if ((certfile = smime_extract_certificate (infile))) {
1080 if ((thepid = smime_invoke (&smimein, NULL, NULL,
1081 -1, fileno (fpout), fileno (fperr),
1082 certfile, NULL, NULL, NULL, NULL, NULL,
1083 SmimeImportCertCommand)) == -1) {
1084 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1087 fputs (buf, smimein);
1088 fputc ('\n', smimein);
1091 mutt_wait_filter (thepid);
1093 mutt_unlink (certfile);
1094 p_delete(&certfile);
1102 mutt_copy_stream (fpout, stdout);
1103 mutt_copy_stream (fperr, stdout);
1112 int smime_verify_sender (HEADER * h)
1114 char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1118 mutt_mktemp (tempfname);
1119 if (!(fpout = safe_fopen (tempfname, "w"))) {
1120 mutt_perror (tempfname);
1124 if (h->security & ENCRYPT)
1125 mutt_copy_message (fpout, Context, h,
1126 M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1127 CH_MIME | CH_WEED | CH_NONEWLINE);
1129 mutt_copy_message (fpout, Context, h, 0, 0);
1135 h->env->from = mutt_expand_aliases (h->env->from);
1136 mbox = h->env->from->mailbox;
1138 else if (h->env->sender) {
1139 h->env->sender = mutt_expand_aliases (h->env->sender);
1140 mbox = h->env->sender->mailbox;
1144 if ((certfile = smime_extract_signer_certificate (tempfname))) {
1145 mutt_unlink (tempfname);
1146 if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1148 mutt_any_key_to_continue (NULL);
1152 mutt_unlink (certfile);
1153 p_delete(&certfile);
1156 mutt_any_key_to_continue (_("no certfile"));
1159 mutt_any_key_to_continue (_("no mbox"));
1161 mutt_unlink (tempfname);
1174 * Creating S/MIME - bodies.
1181 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1182 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1183 int smimeerrfd, const char *fname,
1186 return smime_invoke (smimein, smimeout, smimeerr,
1187 smimeinfd, smimeoutfd, smimeerrfd,
1188 fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1189 SmimeEncryptCommand);
1194 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1195 int smimeinfd, int smimeoutfd, int smimeerrfd,
1198 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1199 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1200 SmimeCertToUse, SmimeIntermediateToUse,
1207 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1209 char buf[LONG_STRING], certfile[LONG_STRING];
1210 char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1211 char smimeinfile[_POSIX_PATH_MAX];
1212 char *cert_start = certlist, *cert_end = certlist;
1213 FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1218 mutt_mktemp (tempfile);
1219 if ((fpout = safe_fopen (tempfile, "w+")) == NULL) {
1220 mutt_perror (tempfile);
1224 mutt_mktemp (smimeerrfile);
1225 if ((smimeerr = safe_fopen (smimeerrfile, "w+")) == NULL) {
1226 mutt_perror (smimeerrfile);
1228 mutt_unlink (tempfile);
1231 mutt_unlink (smimeerrfile);
1233 mutt_mktemp (smimeinfile);
1234 if ((fptmp = safe_fopen (smimeinfile, "w+")) == NULL) {
1235 mutt_perror (smimeinfile);
1236 mutt_unlink (tempfile);
1244 int off = str_len (certfile);
1246 while (*++cert_end && *cert_end != '\n');
1250 snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1251 NONULL (SmimeCertificates), cert_start);
1253 cert_start = cert_end;
1257 /* write a MIME entity */
1258 mutt_write_mime_header (a, fptmp);
1259 fputc ('\n', fptmp);
1260 mutt_write_mime_body (a, fptmp);
1264 smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1265 fileno (fpout), fileno (smimeerr),
1266 smimeinfile, certfile)) == -1) {
1268 mutt_unlink (smimeinfile);
1269 mutt_unlink (certfile);
1275 mutt_wait_filter (thepid);
1276 mutt_unlink (smimeinfile);
1277 mutt_unlink (certfile);
1281 empty = (fgetc (fpout) == EOF);
1286 while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1288 fputs (buf, stdout);
1292 /* pause if there is any error output from SMIME */
1294 mutt_any_key_to_continue (NULL);
1297 /* fatal error while trying to encrypt message */
1299 mutt_any_key_to_continue _("No output from OpenSSL..");
1301 mutt_unlink (tempfile);
1305 t = mutt_new_body ();
1306 t->type = TYPEAPPLICATION;
1307 t->subtype = str_dup ("x-pkcs7-mime");
1308 mutt_set_parameter ("name", "smime.p7m", &t->parameter);
1309 mutt_set_parameter ("smime-type", "enveloped-data", &t->parameter);
1310 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
1312 t->disposition = DISPATTACH;
1313 t->d_filename = str_dup ("smime.p7m");
1314 t->filename = str_dup (tempfile);
1315 t->unlink = 1; /*delete after sending the message */
1325 BODY *smime_sign_message (BODY * a)
1328 char buffer[LONG_STRING];
1329 char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1330 FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1334 char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1336 if (!intermediates) {
1337 mutt_message (_("Warning: Intermediate certificate not found."));
1338 intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
1341 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
1343 mutt_mktemp (filetosign);
1344 if ((sfp = safe_fopen (filetosign, "w+")) == NULL) {
1345 mutt_perror (filetosign);
1349 mutt_mktemp (signedfile);
1350 if ((smimeout = safe_fopen (signedfile, "w+")) == NULL) {
1351 mutt_perror (signedfile);
1353 mutt_unlink (filetosign);
1357 mutt_write_mime_header (a, sfp);
1359 mutt_write_mime_body (a, sfp);
1364 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1365 NONULL (SmimeKeys), SmimeDefaultKey);
1367 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1368 NONULL (SmimeCertificates), SmimeDefaultKey);
1370 snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1371 NONULL (SmimeCertificates), intermediates);
1375 if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1376 -1, fileno (smimeout), -1,
1377 filetosign)) == -1) {
1378 mutt_perror (_("Can't open OpenSSL subprocess!"));
1381 mutt_unlink (signedfile);
1382 mutt_unlink (filetosign);
1385 fputs (SmimePass, smimein);
1386 fputc ('\n', smimein);
1390 mutt_wait_filter (thepid);
1392 /* check for errors from OpenSSL */
1396 while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1398 fputs (buffer, stdout);
1405 empty = (fgetc (smimeout) == EOF);
1408 mutt_unlink (filetosign);
1412 mutt_any_key_to_continue (NULL);
1415 mutt_any_key_to_continue _("No output from OpenSSL...");
1417 mutt_unlink (signedfile);
1418 return (NULL); /* fatal error while signing */
1421 t = mutt_new_body ();
1422 t->type = TYPEMULTIPART;
1423 t->subtype = str_dup ("signed");
1424 t->encoding = ENC7BIT;
1426 t->disposition = DISPINLINE;
1428 mutt_generate_boundary (&t->parameter);
1429 /* check if this can be extracted from private key somehow.... */
1430 mutt_set_parameter ("micalg", "sha1", &t->parameter);
1431 mutt_set_parameter ("protocol", "application/x-pkcs7-signature",
1437 t->parts->next = mutt_new_body ();
1439 t->type = TYPEAPPLICATION;
1440 t->subtype = str_dup ("x-pkcs7-signature");
1441 t->filename = str_dup (signedfile);
1442 t->d_filename = str_dup ("smime.p7s");
1444 t->disposition = DISPATTACH;
1445 t->encoding = ENCBASE64;
1446 t->unlink = 1; /* ok to remove this file after sending. */
1458 * Handling S/MIME - bodies.
1467 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1468 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1469 int smimeerrfd, const char *fname,
1470 const char *sig_fname, int opaque)
1472 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1473 smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1474 (opaque ? SmimeVerifyOpaqueCommand :
1475 SmimeVerifyCommand));
1480 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1481 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1482 int smimeerrfd, const char *fname)
1484 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1485 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1486 SmimeCertToUse, NULL, SmimeDecryptCommand);
1491 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1493 char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1494 FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1499 size_t tmplength = 0;
1500 int origType = sigbdy->type;
1501 char *savePrefix = NULL;
1504 snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1506 /* decode to a tempfile, saving the original destination */
1508 if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1509 mutt_perror (signedfile);
1512 /* decoding the attachment changes the size and offset, so save a copy
1513 * of the "real" values now, and restore them after processing
1515 tmplength = sigbdy->length;
1516 tmpoffset = sigbdy->offset;
1518 /* if we are decoding binary bodies, we don't want to prefix each
1519 * line with the prefix or else the data will get corrupted.
1521 savePrefix = s->prefix;
1524 mutt_decode_attachment (sigbdy, s);
1526 sigbdy->length = ftello (s->fpout);
1530 /* restore final destination and substitute the tempfile for input */
1533 s->fpin = fopen (signedfile, "r");
1535 /* restore the prefix */
1536 s->prefix = savePrefix;
1538 sigbdy->type = origType;
1541 mutt_mktemp (smimeerrfile);
1542 if (!(smimeerr = safe_fopen (smimeerrfile, "w+"))) {
1543 mutt_perror (smimeerrfile);
1544 mutt_unlink (signedfile);
1548 crypt_current_time (s, "OpenSSL");
1550 if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1551 -1, -1, fileno (smimeerr),
1552 tempfile, signedfile, 0)) != -1) {
1556 if (mutt_wait_filter (thepid))
1566 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1567 if (linelen && !str_casecmp (line, "verification successful"))
1576 mutt_copy_stream (smimeerr, s->fpout);
1579 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1581 mutt_unlink (signedfile);
1582 mutt_unlink (smimeerrfile);
1584 sigbdy->length = tmplength;
1585 sigbdy->offset = tmpoffset;
1587 /* restore the original source stream */
1600 This handles application/pkcs7-mime which can either be a signed
1601 or an encrypted message.
1604 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1609 char buf[HUGE_STRING];
1610 char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1611 char tmpfname[_POSIX_PATH_MAX];
1612 char tmptmpfname[_POSIX_PATH_MAX];
1613 FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1614 FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1618 unsigned int type = mutt_is_application_smime (m);
1620 if (!(type & APPLICATION_SMIME))
1623 mutt_mktemp (outfile);
1624 if ((smimeout = safe_fopen (outfile, "w+")) == NULL) {
1625 mutt_perror (outfile);
1629 mutt_mktemp (errfile);
1630 if ((smimeerr = safe_fopen (errfile, "w+")) == NULL) {
1631 mutt_perror (errfile);
1636 mutt_unlink (errfile);
1639 mutt_mktemp (tmpfname);
1640 if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL) {
1641 mutt_perror (tmpfname);
1649 fseeko (s->fpin, m->offset, 0);
1650 last_pos = m->offset;
1652 mutt_copy_bytes (s->fpin, tmpfp, m->length);
1657 if ((type & ENCRYPT) &&
1658 (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1659 fileno (smimeout), fileno (smimeerr),
1663 mutt_unlink (tmpfname);
1664 if (s->flags & M_DISPLAY)
1665 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1668 else if ((type & SIGNOPAQUE) &&
1669 (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1671 fileno (smimeerr), NULL, tmpfname,
1672 SIGNOPAQUE)) == -1) {
1675 mutt_unlink (tmpfname);
1676 if (s->flags & M_DISPLAY)
1677 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1682 if (type & ENCRYPT) {
1683 if (!smime_valid_passphrase ())
1684 smime_void_passphrase ();
1685 fputs (SmimePass, smimein);
1686 fputc ('\n', smimein);
1691 mutt_wait_filter (thepid);
1692 mutt_unlink (tmpfname);
1695 if (s->flags & M_DISPLAY) {
1698 if ((c = fgetc (smimeerr)) != EOF) {
1699 ungetc (c, smimeerr);
1701 crypt_current_time (s, "OpenSSL");
1702 mutt_copy_stream (smimeerr, s->fpout);
1703 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1707 state_attach_puts (_("[-- The following data is S/MIME"
1708 " encrypted --]\n"), s);
1710 state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1721 mutt_mktemp (tmptmpfname);
1722 if ((fpout = safe_fopen (tmptmpfname, "w+")) == NULL) {
1723 mutt_perror (tmptmpfname);
1729 while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1730 len = str_len (buf);
1731 if (len > 1 && buf[len - 2] == '\r') {
1732 buf[len - 2] = '\n';
1733 buf[len - 1] = '\0';
1741 if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1742 fstat (fileno (fpout), &info);
1743 p->length = info.st_size - p->offset;
1745 mutt_parse_part (fpout, p);
1748 tmpfp_buffer = s->fpin;
1750 mutt_body_handler (p, s);
1751 s->fpin = tmpfp_buffer;
1757 mutt_unlink (outfile);
1761 mutt_unlink (tmptmpfname);
1766 if (s->flags & M_DISPLAY) {
1768 state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1770 state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1773 if (type & SIGNOPAQUE) {
1780 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1781 if (linelen && !str_casecmp (line, "verification successful"))
1786 m->goodsig = p->goodsig;
1787 m->badsig = p->badsig;
1798 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1802 char tempfile[_POSIX_PATH_MAX];
1804 long tmpoffset = b->offset;
1805 size_t tmplength = b->length;
1806 int origType = b->type;
1810 if (!mutt_is_application_smime (b))
1816 memset (&s, 0, sizeof (s));
1818 fseeko (s.fpin, b->offset, 0);
1820 mutt_mktemp (tempfile);
1821 if ((tmpfp = safe_fopen (tempfile, "w+")) == NULL) {
1822 mutt_perror (tempfile);
1826 mutt_unlink (tempfile);
1828 mutt_decode_attachment (b, &s);
1830 b->length = ftello (s.fpout);
1836 mutt_mktemp (tempfile);
1837 if ((*fpout = safe_fopen (tempfile, "w+")) == NULL) {
1838 mutt_perror (tempfile);
1842 mutt_unlink (tempfile);
1844 if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1849 (*cur)->goodsig = b->goodsig;
1850 (*cur)->badsig = b->badsig;
1854 b->length = tmplength;
1855 b->offset = tmpoffset;
1857 safe_fclose (&tmpfp);
1864 int smime_application_smime_handler (BODY * m, STATE * s)
1866 return smime_handle_entity (m, s, NULL) ? 0 : -1;
1869 int smime_send_menu (HEADER * msg, int *redraw)
1873 if (!(WithCrypto & APPLICATION_SMIME))
1874 return msg->security;
1876 switch (mutt_multi_choice
1878 ("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1880 case 1: /* (e)ncrypt */
1881 msg->security |= ENCRYPT;
1882 msg->security &= ~SIGN;
1885 case 3: /* encrypt (w)ith */
1888 msg->security |= ENCRYPT;
1891 /* I use "dra" because "123" is recognized anyway */
1892 switch (mutt_multi_choice (_("Choose algorithm family:"
1893 " 1: DES, 2: RC2, 3: AES,"
1894 " or (c)lear? "), _("drac"))) {
1896 switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
1899 str_replace (&SmimeCryptAlg, "des");
1902 str_replace (&SmimeCryptAlg, "des3");
1908 switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
1911 str_replace (&SmimeCryptAlg, "rc2-40");
1914 str_replace (&SmimeCryptAlg, "rc2-64");
1917 str_replace (&SmimeCryptAlg, "rc2-128");
1923 switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
1926 str_replace (&SmimeCryptAlg, "aes128");
1929 str_replace (&SmimeCryptAlg, "aes192");
1932 str_replace (&SmimeCryptAlg, "aes256");
1937 case 4: /* (c)lear */
1938 p_delete(&SmimeCryptAlg);
1940 case -1: /* Ctrl-G or Enter */
1944 } while (choice == -1);
1948 case 2: /* (s)ign */
1950 if (!SmimeDefaultKey)
1951 mutt_message (_("Can't sign: No key specified. Use Sign As."));
1954 msg->security |= SIGN;
1955 msg->security &= ~ENCRYPT;
1959 case 4: /* sign (a)s */
1961 if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1962 str_replace (&SmimeDefaultKey, p);
1964 msg->security |= SIGN;
1966 /* probably need a different passphrase */
1967 crypt_smime_void_passphrase ();
1971 msg->security &= ~SIGN;
1974 *redraw = REDRAW_FULL;
1977 case 5: /* (b)oth */
1978 msg->security |= (ENCRYPT | SIGN);
1981 case 6: /* (f)orget it */
1982 case 7: /* (c)lear */
1987 if (msg->security && msg->security != APPLICATION_SMIME)
1988 msg->security |= APPLICATION_SMIME;
1992 return (msg->security);
1996 #endif /* CRYPT_BACKEND_CLASSIC_SMIME */