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.
17 #include "mutt_curses.h"
18 #include "mutt_menu.h"
26 #include "lib/debug.h"
40 #ifdef HAVE_SYS_TIME_H
41 # include <sys/time.h>
44 #ifdef HAVE_SYS_RESOURCE_H
45 # include <sys/resource.h>
48 #ifdef CRYPT_BACKEND_CLASSIC_SMIME
50 #include "mutt_crypt.h"
52 struct smime_command_context {
53 const char *key; /* %k */
54 const char *cryptalg; /* %a */
55 const char *fname; /* %f */
56 const char *sig_fname; /* %s */
57 const char *certificates; /* %c */
58 const char *intermediates; /* %i */
67 char trust; /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
68 short public; /* 1=public 0=private */
72 char SmimePass[STRING];
73 time_t SmimeExptime = 0; /* when does the cached passphrase expire? */
76 static char SmimeKeyToUse[_POSIX_PATH_MAX] = { 0 };
77 static char SmimeCertToUse[_POSIX_PATH_MAX];
78 static char SmimeIntermediateToUse[_POSIX_PATH_MAX];
82 * Queries and passphrase handling.
88 /* these are copies from pgp.c */
91 void smime_void_passphrase (void)
93 memset (SmimePass, 0, sizeof (SmimePass));
97 int smime_valid_passphrase (void)
99 time_t now = time (NULL);
101 if (now < SmimeExptime)
102 /* Use cached copy. */
105 smime_void_passphrase ();
107 if (mutt_get_field_unbuffered (_("Enter SMIME passphrase:"), SmimePass,
108 sizeof (SmimePass), M_PASS) == 0) {
109 SmimeExptime = time (NULL) + SmimeTimeout;
120 * The OpenSSL interface
123 /* This is almost identical to ppgp's invoking interface. */
125 static const char *_mutt_fmt_smime_command (char *dest,
130 const char *ifstring,
131 const char *elsestring,
136 struct smime_command_context *cctx = (struct smime_command_context *) data;
137 int optional = (flags & M_FORMAT_OPTIONAL);
143 char path[_POSIX_PATH_MAX];
144 char buf1[LONG_STRING], buf2[LONG_STRING];
147 strfcpy (path, NONULL (SmimeCALocation), sizeof (path));
148 mutt_expand_path (path, sizeof (path));
149 mutt_quote_filename (buf1, sizeof (buf1), path);
151 if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
152 snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
154 snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
156 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
157 snprintf (dest, destlen, fmt, buf2);
159 else if (!SmimeCALocation)
165 { /* certificate (list) */
167 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
168 snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
170 else if (!cctx->certificates)
176 { /* intermediate certificates */
178 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
179 snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
181 else if (!cctx->intermediates)
187 { /* detached signature */
189 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
190 snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
192 else if (!cctx->sig_fname)
200 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
201 snprintf (dest, destlen, fmt, NONULL (cctx->key));
209 { /* algorithm for encryption */
211 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
212 snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
220 { /* file to process */
222 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
223 snprintf (dest, destlen, fmt, NONULL (cctx->fname));
225 else if (!cctx->fname)
236 mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
238 else if (flags & M_FORMAT_OPTIONAL)
239 mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
247 static void mutt_smime_command (char *d, size_t dlen,
248 struct smime_command_context *cctx,
251 mutt_FormatString (d, dlen, NONULL (fmt), _mutt_fmt_smime_command,
252 (unsigned long) cctx, 0);
253 debug_print (2, ("%s\n", d));
256 static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
257 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
258 int smimeerrfd, const char *fname,
259 const char *sig_fname, const char *cryptalg,
260 const char *key, const char *certificates,
261 const char *intermediates, const char *format)
263 struct smime_command_context cctx;
264 char cmd[HUGE_STRING];
266 memset (&cctx, 0, sizeof (cctx));
268 if (!format || !*format)
272 cctx.sig_fname = sig_fname;
274 cctx.cryptalg = cryptalg;
275 cctx.certificates = certificates;
276 cctx.intermediates = intermediates;
278 mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
280 return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
281 smimeinfd, smimeoutfd, smimeerrfd);
290 * Key and certificate handling.
296 Search the certificate index for given mailbox.
297 return certificate file name.
300 static void smime_entry (char *s, size_t l, MUTTMENU * menu, int num)
302 smime_id *Table = (smime_id *) menu->data;
303 smime_id this = Table[num];
306 switch (this.trust) {
308 truststate = N_("Trusted ");
311 truststate = N_("Verified ");
314 truststate = N_("Unverified");
317 truststate = N_("Expired ");
320 truststate = N_("Revoked ");
323 truststate = N_("Invalid ");
326 truststate = N_("Unknown ");
329 snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
330 truststate, this.email, this.nick);
332 snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
333 this.email, this.nick);
340 char *smime_ask_for_key (char *prompt, char *mailbox, short public)
344 long cert_num; /* Will contain the number of certificates.
345 * To be able to get it, the .index file will be read twice... */
346 char index_file[_POSIX_PATH_MAX];
348 char buf[LONG_STRING];
349 char fields[5][STRING];
350 int numFields, hash_suffix, done, cur; /* The current entry */
353 char helpstr[HUGE_STRING * 3];
358 prompt = _("Enter keyID: ");
359 snprintf (index_file, sizeof (index_file), "%s/.index",
360 public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));
362 index = fopen (index_file, "r");
364 mutt_perror (index_file);
369 while (!feof (index)) {
370 if (fgets (buf, sizeof (buf), index))
377 if (mutt_get_field (prompt, qry, sizeof (qry), 0))
379 snprintf (title, sizeof (title),
380 _("S/MIME certificates matching \"%s\"."), qry);
383 index = fopen (index_file, "r");
385 mutt_perror (index_file);
390 Table = safe_calloc (cert_num, sizeof (smime_id));
391 while (!feof (index)) {
393 fscanf (index, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
394 fields[0], &hash, &hash_suffix, fields[2]);
396 fscanf (index, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
397 fields[3], fields[4]);
399 /* 0=email 1=name 2=nick 3=intermediate 4=trust */
403 /* Check if query matches this certificate */
404 if (!str_isstr (fields[0], qry) && !str_isstr (fields[2], qry))
407 Table[cur].hash = hash;
408 Table[cur].suffix = hash_suffix;
409 strncpy (Table[cur].email, fields[0], sizeof (Table[cur].email));
410 strncpy (Table[cur].nick, fields[2], sizeof (Table[cur].nick));
411 Table[cur].trust = *fields[4];
412 Table[cur].public = public;
418 /* Make Helpstring */
420 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT);
421 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
422 mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME,
423 OP_GENERIC_SELECT_ENTRY);
424 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
425 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
426 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
428 /* Create the menu */
429 menu = mutt_new_menu ();
431 menu->make_entry = smime_entry;
432 menu->menu = MENU_SMIME;
433 menu->help = helpstr;
436 /* sorting keys might be done later - TODO */
443 switch (mutt_menuLoop (menu)) {
444 case OP_GENERIC_SELECT_ENTRY:
456 fname = safe_malloc (13); /* Hash + '.' + Suffix + \0 */
457 sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
462 mutt_menuDestroy (&menu);
464 set_option (OPTNEEDREDRAW);
473 char *smime_get_field_from_db (char *mailbox, char *query, short public,
476 int addr_len, query_len, found = 0, ask = 0, choice = 0;
477 char cert_path[_POSIX_PATH_MAX];
478 char buf[LONG_STRING], prompt[STRING];
479 char fields[5][STRING];
483 char key_trust_level = 0;
486 if (!mailbox && !query)
489 addr_len = mailbox ? mutt_strlen (mailbox) : 0;
490 query_len = query ? mutt_strlen (query) : 0;
494 /* index-file format:
495 mailbox certfile label issuer_certfile trust_flags\n
497 certfile is a hash value generated by openssl.
498 Note that this was done according to the OpenSSL
499 specs on their CA-directory.
502 snprintf (cert_path, sizeof (cert_path), "%s/.index",
503 (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));
505 if (!stat (cert_path, &info)) {
506 if ((fp = safe_fopen (cert_path, "r")) == NULL) {
507 mutt_perror (cert_path);
511 while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
512 if (mailbox && !(safe_strncasecmp (mailbox, buf, addr_len))) {
513 numFields = sscanf (buf,
514 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
515 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
516 MUTT_FORMAT (STRING) "\n",
517 fields[0], fields[1],
518 fields[2], fields[3], fields[4]);
521 if (mailbox && public &&
523 *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
527 if (public && *fields[4] == 'u')
528 snprintf (prompt, sizeof (prompt),
530 ("ID %s is unverified. Do you want to use it for %s ?"),
532 else if (public && *fields[4] == 'v')
533 snprintf (prompt, sizeof (prompt),
534 _("Use (untrusted!) ID %s for %s ?"),
537 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
541 if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
547 else if (choice == M_NO) {
551 else if (choice == M_YES) {
552 strfcpy (key, fields[1], sizeof (key));
559 key_trust_level = *fields[4];
560 strfcpy (key, fields[1], sizeof (key));
565 numFields = sscanf (buf,
566 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
567 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
568 MUTT_FORMAT (STRING) "\n",
569 fields[0], fields[1],
570 fields[2], fields[3], fields[4]);
572 /* query = label: return certificate. */
573 if (numFields >= 3 &&
574 !(safe_strncasecmp (query, fields[2], query_len))) {
576 strfcpy (key, fields[1], sizeof (key));
578 /* query = certificate: return intermediate certificate. */
579 else if (numFields >= 4 &&
580 !(safe_strncasecmp (query, fields[1], query_len))) {
582 strfcpy (key, fields[3], sizeof (key));
589 if (public && *fields[4] == 'u')
590 snprintf (prompt, sizeof (prompt),
591 _("ID %s is unverified. Do you want to use it for %s ?"),
593 else if (public && *fields[4] == 'v')
594 snprintf (prompt, sizeof (prompt),
595 _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
597 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
599 choice = mutt_yesorno (prompt, M_NO);
600 if (choice == -1 || choice == M_NO)
603 else if (key_trust_level && may_ask) {
604 if (key_trust_level == 'u') {
605 snprintf (prompt, sizeof (prompt),
606 _("ID %s is unverified. Do you want to use it for %s ?"),
608 choice = mutt_yesorno (prompt, M_NO);
612 else if (key_trust_level == 'v') {
614 ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
622 /* Note: safe_strdup ("") returns NULL. */
623 return safe_strdup (key);
630 This sets the '*ToUse' variables for an upcoming decryption, where
631 the reuquired key is different from SmimeDefaultKey.
634 void _smime_getkeys (char *mailbox)
639 k = smime_get_field_from_db (mailbox, NULL, 0, 1);
642 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
643 k = smime_ask_for_key (buf, mailbox, 0);
647 /* the key used last time. */
648 if (*SmimeKeyToUse &&
649 !safe_strcasecmp (k, SmimeKeyToUse + mutt_strlen (SmimeKeys) + 1)) {
654 smime_void_passphrase ();
656 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
657 NONULL (SmimeKeys), k);
659 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
660 NONULL (SmimeCertificates), k);
662 if (safe_strcasecmp (k, SmimeDefaultKey))
663 smime_void_passphrase ();
669 if (*SmimeKeyToUse) {
670 if (!safe_strcasecmp (SmimeDefaultKey,
671 SmimeKeyToUse + mutt_strlen (SmimeKeys) + 1))
674 smime_void_passphrase ();
677 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
678 NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
680 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
681 NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
684 void smime_getkeys (ENVELOPE * env)
689 if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
690 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
691 NONULL (SmimeKeys), SmimeDefaultKey);
693 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
694 NONULL (SmimeCertificates), SmimeDefaultKey);
699 for (t = env->to; !found && t; t = t->next)
700 if (mutt_addr_is_user (t)) {
702 _smime_getkeys (t->mailbox);
704 for (t = env->cc; !found && t; t = t->next)
705 if (mutt_addr_is_user (t)) {
707 _smime_getkeys (t->mailbox);
709 if (!found && (t = mutt_default_from ())) {
710 _smime_getkeys (t->mailbox);
711 rfc822_free_address (&t);
715 /* This routine attempts to find the keyids of the recipients of a message.
716 * It returns NULL if any of the keys can not be found.
719 char *smime_findKeys (ADDRESS * to, ADDRESS * cc, ADDRESS * bcc)
721 char *keyID, *keylist = NULL;
722 size_t keylist_size = 0;
723 size_t keylist_used = 0;
724 ADDRESS *tmp = NULL, *addr = NULL;
725 ADDRESS **last = &tmp;
729 const char *fqdn = mutt_fqdn (1);
731 for (i = 0; i < 3; i++) {
746 *last = rfc822_cpy_adr (p);
748 last = &((*last)->next);
752 rfc822_qualify (tmp, fqdn);
754 tmp = mutt_remove_duplicates (tmp);
756 for (p = tmp; p; p = p->next) {
757 char buf[LONG_STRING];
761 if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
762 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
763 keyID = smime_ask_for_key (buf, q->mailbox, 1);
766 mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
768 rfc822_free_address (&tmp);
769 rfc822_free_address (&addr);
773 keylist_size += mutt_strlen (keyID) + 2;
774 safe_realloc (&keylist, keylist_size);
775 sprintf (keylist + keylist_used, "%s\n", keyID); /* __SPRINTF_CHECKED__ */
776 keylist_used = mutt_strlen (keylist);
778 rfc822_free_address (&addr);
781 rfc822_free_address (&tmp);
790 static int smime_handle_cert_email (char *certificate, char *mailbox,
791 int copy, char ***buffer, int *num)
793 FILE *fpout = NULL, *fperr = NULL;
794 char tmpfname[_POSIX_PATH_MAX];
796 int ret = -1, count = 0;
799 mutt_mktemp (tmpfname);
800 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
801 mutt_perror (tmpfname);
804 mutt_unlink (tmpfname);
806 mutt_mktemp (tmpfname);
807 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
809 mutt_perror (tmpfname);
812 mutt_unlink (tmpfname);
814 if ((thepid = smime_invoke (NULL, NULL, NULL,
815 -1, fileno (fpout), fileno (fperr),
816 certificate, NULL, NULL, NULL, NULL, NULL,
817 SmimeGetCertEmailCommand)) == -1) {
818 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
824 mutt_wait_filter (thepid);
832 while ((fgets (email, sizeof (email), fpout))) {
833 *(email + mutt_strlen (email) - 1) = '\0';
834 if (safe_strncasecmp (email, mailbox, mutt_strlen (mailbox)) == 0)
837 ret = ret < 0 ? 0 : ret;
843 mutt_copy_stream (fperr, stdout);
844 mutt_any_key_to_continue (_
845 ("Error: unable to create OpenSSL subprocess!"));
853 if (copy && buffer && num) {
855 *buffer = safe_calloc (sizeof (char *), count);
859 while ((fgets (email, sizeof (email), fpout))) {
860 *(email + mutt_strlen (email) - 1) = '\0';
861 (*buffer)[count] = safe_calloc (1, mutt_strlen (email) + 1);
862 strncpy ((*buffer)[count], email, mutt_strlen (email));
877 static char *smime_extract_certificate (char *infile)
879 FILE *fpout = NULL, *fperr = NULL;
880 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
881 char tmpfname[_POSIX_PATH_MAX];
886 mutt_mktemp (tmpfname);
887 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
888 mutt_perror (tmpfname);
891 mutt_unlink (tmpfname);
893 mutt_mktemp (pk7out);
894 if ((fpout = safe_fopen (pk7out, "w+")) == NULL) {
896 mutt_perror (pk7out);
900 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
901 extract the full set of certificates directly.
903 if ((thepid = smime_invoke (NULL, NULL, NULL,
904 -1, fileno (fpout), fileno (fperr),
905 infile, NULL, NULL, NULL, NULL, NULL,
906 SmimePk7outCommand)) == -1) {
907 mutt_any_key_to_continue (_
908 ("Error: unable to create OpenSSL subprocess!"));
911 mutt_unlink (pk7out);
915 mutt_wait_filter (thepid);
922 empty = (fgetc (fpout) == EOF);
924 mutt_perror (pk7out);
925 mutt_copy_stream (fperr, stdout);
928 mutt_unlink (pk7out);
935 mutt_mktemp (certfile);
936 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
938 mutt_unlink (pk7out);
939 mutt_perror (certfile);
943 /* Step 2: Extract the certificates from a PKCS#7 structure.
945 if ((thepid = smime_invoke (NULL, NULL, NULL,
946 -1, fileno (fpout), fileno (fperr),
947 pk7out, NULL, NULL, NULL, NULL, NULL,
948 SmimeGetCertCommand)) == -1) {
949 mutt_any_key_to_continue (_
950 ("Error: unable to create OpenSSL subprocess!"));
953 mutt_unlink (pk7out);
954 mutt_unlink (certfile);
958 mutt_wait_filter (thepid);
960 mutt_unlink (pk7out);
966 empty = (fgetc (fpout) == EOF);
968 mutt_copy_stream (fperr, stdout);
971 mutt_unlink (certfile);
978 return safe_strdup (certfile);
981 static char *smime_extract_signer_certificate (char *infile)
983 FILE *fpout = NULL, *fperr = NULL;
984 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
985 char tmpfname[_POSIX_PATH_MAX];
990 mutt_mktemp (tmpfname);
991 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
992 mutt_perror (tmpfname);
995 mutt_unlink (tmpfname);
998 mutt_mktemp (certfile);
999 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
1001 mutt_perror (certfile);
1005 /* Extract signer's certificate
1007 if ((thepid = smime_invoke (NULL, NULL, NULL,
1008 -1, -1, fileno (fperr),
1009 infile, NULL, NULL, NULL, certfile, NULL,
1010 SmimeGetSignerCertCommand)) == -1) {
1011 mutt_any_key_to_continue (_
1012 ("Error: unable to create OpenSSL subprocess!"));
1015 mutt_unlink (pk7out);
1016 mutt_unlink (certfile);
1020 mutt_wait_filter (thepid);
1026 empty = (fgetc (fpout) == EOF);
1029 mutt_copy_stream (fperr, stdout);
1030 mutt_any_key_to_continue (NULL);
1033 mutt_unlink (certfile);
1040 return safe_strdup (certfile);
1046 /* Add a certificate and update index file (externally). */
1048 void smime_invoke_import (char *infile, char *mailbox)
1050 char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1051 FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1054 mutt_mktemp (tmpfname);
1055 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1056 mutt_perror (tmpfname);
1059 mutt_unlink (tmpfname);
1061 mutt_mktemp (tmpfname);
1062 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
1064 mutt_perror (tmpfname);
1067 mutt_unlink (tmpfname);
1071 if (option (OPTASKCERTLABEL))
1072 mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1075 if ((certfile = smime_extract_certificate (infile))) {
1078 if ((thepid = smime_invoke (&smimein, NULL, NULL,
1079 -1, fileno (fpout), fileno (fperr),
1080 certfile, NULL, NULL, NULL, NULL, NULL,
1081 SmimeImportCertCommand)) == -1) {
1082 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1085 fputs (buf, smimein);
1086 fputc ('\n', smimein);
1089 mutt_wait_filter (thepid);
1091 mutt_unlink (certfile);
1100 mutt_copy_stream (fpout, stdout);
1101 mutt_copy_stream (fperr, stdout);
1110 int smime_verify_sender (HEADER * h)
1112 char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1116 mutt_mktemp (tempfname);
1117 if (!(fpout = safe_fopen (tempfname, "w"))) {
1118 mutt_perror (tempfname);
1122 if (h->security & ENCRYPT)
1123 mutt_copy_message (fpout, Context, h,
1124 M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1125 CH_MIME | CH_WEED | CH_NONEWLINE);
1127 mutt_copy_message (fpout, Context, h, 0, 0);
1133 h->env->from = mutt_expand_aliases (h->env->from);
1134 mbox = h->env->from->mailbox;
1136 else if (h->env->sender) {
1137 h->env->sender = mutt_expand_aliases (h->env->sender);
1138 mbox = h->env->sender->mailbox;
1142 if ((certfile = smime_extract_signer_certificate (tempfname))) {
1143 mutt_unlink (tempfname);
1144 if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1146 mutt_any_key_to_continue (NULL);
1150 mutt_unlink (certfile);
1154 mutt_any_key_to_continue (_("no certfile"));
1157 mutt_any_key_to_continue (_("no mbox"));
1159 mutt_unlink (tempfname);
1172 * Creating S/MIME - bodies.
1179 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1180 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1181 int smimeerrfd, const char *fname,
1184 return smime_invoke (smimein, smimeout, smimeerr,
1185 smimeinfd, smimeoutfd, smimeerrfd,
1186 fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1187 SmimeEncryptCommand);
1192 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1193 int smimeinfd, int smimeoutfd, int smimeerrfd,
1196 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1197 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1198 SmimeCertToUse, SmimeIntermediateToUse,
1205 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1207 char buf[LONG_STRING], certfile[LONG_STRING];
1208 char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1209 char smimeinfile[_POSIX_PATH_MAX];
1210 char *cert_start = certlist, *cert_end = certlist;
1211 FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1216 mutt_mktemp (tempfile);
1217 if ((fpout = safe_fopen (tempfile, "w+")) == NULL) {
1218 mutt_perror (tempfile);
1222 mutt_mktemp (smimeerrfile);
1223 if ((smimeerr = safe_fopen (smimeerrfile, "w+")) == NULL) {
1224 mutt_perror (smimeerrfile);
1226 mutt_unlink (tempfile);
1229 mutt_unlink (smimeerrfile);
1231 mutt_mktemp (smimeinfile);
1232 if ((fptmp = safe_fopen (smimeinfile, "w+")) == NULL) {
1233 mutt_perror (smimeinfile);
1234 mutt_unlink (tempfile);
1242 int off = mutt_strlen (certfile);
1244 while (*++cert_end && *cert_end != '\n');
1248 snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1249 NONULL (SmimeCertificates), cert_start);
1251 cert_start = cert_end;
1255 /* write a MIME entity */
1256 mutt_write_mime_header (a, fptmp);
1257 fputc ('\n', fptmp);
1258 mutt_write_mime_body (a, fptmp);
1262 smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1263 fileno (fpout), fileno (smimeerr),
1264 smimeinfile, certfile)) == -1) {
1266 mutt_unlink (smimeinfile);
1267 mutt_unlink (certfile);
1273 mutt_wait_filter (thepid);
1274 mutt_unlink (smimeinfile);
1275 mutt_unlink (certfile);
1279 empty = (fgetc (fpout) == EOF);
1284 while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1286 fputs (buf, stdout);
1290 /* pause if there is any error output from SMIME */
1292 mutt_any_key_to_continue (NULL);
1295 /* fatal error while trying to encrypt message */
1297 mutt_any_key_to_continue _("No output from OpenSSL..");
1299 mutt_unlink (tempfile);
1303 t = mutt_new_body ();
1304 t->type = TYPEAPPLICATION;
1305 t->subtype = safe_strdup ("x-pkcs7-mime");
1306 mutt_set_parameter ("name", "smime.p7m", &t->parameter);
1307 mutt_set_parameter ("smime-type", "enveloped-data", &t->parameter);
1308 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
1310 t->disposition = DISPATTACH;
1311 t->d_filename = safe_strdup ("smime.p7m");
1312 t->filename = safe_strdup (tempfile);
1313 t->unlink = 1; /*delete after sending the message */
1323 BODY *smime_sign_message (BODY * a)
1326 char buffer[LONG_STRING];
1327 char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1328 FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1332 char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1334 if (!intermediates) {
1335 mutt_message (_("Warning: Intermediate certificate not found."));
1336 intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
1339 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
1341 mutt_mktemp (filetosign);
1342 if ((sfp = safe_fopen (filetosign, "w+")) == NULL) {
1343 mutt_perror (filetosign);
1347 mutt_mktemp (signedfile);
1348 if ((smimeout = safe_fopen (signedfile, "w+")) == NULL) {
1349 mutt_perror (signedfile);
1351 mutt_unlink (filetosign);
1355 mutt_write_mime_header (a, sfp);
1357 mutt_write_mime_body (a, sfp);
1362 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1363 NONULL (SmimeKeys), SmimeDefaultKey);
1365 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1366 NONULL (SmimeCertificates), SmimeDefaultKey);
1368 snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1369 NONULL (SmimeCertificates), intermediates);
1373 if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1374 -1, fileno (smimeout), -1,
1375 filetosign)) == -1) {
1376 mutt_perror (_("Can't open OpenSSL subprocess!"));
1379 mutt_unlink (signedfile);
1380 mutt_unlink (filetosign);
1383 fputs (SmimePass, smimein);
1384 fputc ('\n', smimein);
1388 mutt_wait_filter (thepid);
1390 /* check for errors from OpenSSL */
1394 while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1396 fputs (buffer, stdout);
1403 empty = (fgetc (smimeout) == EOF);
1406 mutt_unlink (filetosign);
1410 mutt_any_key_to_continue (NULL);
1413 mutt_any_key_to_continue _("No output from OpenSSL...");
1415 mutt_unlink (signedfile);
1416 return (NULL); /* fatal error while signing */
1419 t = mutt_new_body ();
1420 t->type = TYPEMULTIPART;
1421 t->subtype = safe_strdup ("signed");
1422 t->encoding = ENC7BIT;
1424 t->disposition = DISPINLINE;
1426 mutt_generate_boundary (&t->parameter);
1427 /* check if this can be extracted from private key somehow.... */
1428 mutt_set_parameter ("micalg", "sha1", &t->parameter);
1429 mutt_set_parameter ("protocol", "application/x-pkcs7-signature",
1435 t->parts->next = mutt_new_body ();
1437 t->type = TYPEAPPLICATION;
1438 t->subtype = safe_strdup ("x-pkcs7-signature");
1439 t->filename = safe_strdup (signedfile);
1440 t->d_filename = safe_strdup ("smime.p7s");
1442 t->disposition = DISPATTACH;
1443 t->encoding = ENCBASE64;
1444 t->unlink = 1; /* ok to remove this file after sending. */
1456 * Handling S/MIME - bodies.
1465 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1466 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1467 int smimeerrfd, const char *fname,
1468 const char *sig_fname, int opaque)
1470 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1471 smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1472 (opaque ? SmimeVerifyOpaqueCommand :
1473 SmimeVerifyCommand));
1478 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1479 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1480 int smimeerrfd, const char *fname)
1482 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1483 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1484 SmimeCertToUse, NULL, SmimeDecryptCommand);
1489 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1491 char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1492 FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1497 size_t tmplength = 0;
1498 int origType = sigbdy->type;
1499 char *savePrefix = NULL;
1502 snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1504 /* decode to a tempfile, saving the original destination */
1506 if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1507 mutt_perror (signedfile);
1510 /* decoding the attachment changes the size and offset, so save a copy
1511 * of the "real" values now, and restore them after processing
1513 tmplength = sigbdy->length;
1514 tmpoffset = sigbdy->offset;
1516 /* if we are decoding binary bodies, we don't want to prefix each
1517 * line with the prefix or else the data will get corrupted.
1519 savePrefix = s->prefix;
1522 mutt_decode_attachment (sigbdy, s);
1524 sigbdy->length = ftell (s->fpout);
1528 /* restore final destination and substitute the tempfile for input */
1531 s->fpin = fopen (signedfile, "r");
1533 /* restore the prefix */
1534 s->prefix = savePrefix;
1536 sigbdy->type = origType;
1539 mutt_mktemp (smimeerrfile);
1540 if (!(smimeerr = safe_fopen (smimeerrfile, "w+"))) {
1541 mutt_perror (smimeerrfile);
1542 mutt_unlink (signedfile);
1546 crypt_current_time (s, "OpenSSL");
1548 if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1549 -1, -1, fileno (smimeerr),
1550 tempfile, signedfile, 0)) != -1) {
1554 if (mutt_wait_filter (thepid))
1564 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1565 if (linelen && !safe_strcasecmp (line, "verification successful"))
1574 mutt_copy_stream (smimeerr, s->fpout);
1577 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1579 mutt_unlink (signedfile);
1580 mutt_unlink (smimeerrfile);
1582 sigbdy->length = tmplength;
1583 sigbdy->offset = tmpoffset;
1585 /* restore the original source stream */
1598 This handles application/pkcs7-mime which can either be a signed
1599 or an encrypted message.
1602 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1607 char buf[HUGE_STRING];
1608 char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1609 char tmpfname[_POSIX_PATH_MAX];
1610 char tmptmpfname[_POSIX_PATH_MAX];
1611 FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1612 FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1616 unsigned int type = mutt_is_application_smime (m);
1618 if (!(type & APPLICATION_SMIME))
1621 mutt_mktemp (outfile);
1622 if ((smimeout = safe_fopen (outfile, "w+")) == NULL) {
1623 mutt_perror (outfile);
1627 mutt_mktemp (errfile);
1628 if ((smimeerr = safe_fopen (errfile, "w+")) == NULL) {
1629 mutt_perror (errfile);
1634 mutt_unlink (errfile);
1637 mutt_mktemp (tmpfname);
1638 if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL) {
1639 mutt_perror (tmpfname);
1647 fseek (s->fpin, m->offset, 0);
1648 last_pos = m->offset;
1650 mutt_copy_bytes (s->fpin, tmpfp, m->length);
1655 if ((type & ENCRYPT) &&
1656 (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1657 fileno (smimeout), fileno (smimeerr),
1661 mutt_unlink (tmpfname);
1662 if (s->flags & M_DISPLAY)
1663 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1666 else if ((type & SIGNOPAQUE) &&
1667 (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1669 fileno (smimeerr), NULL, tmpfname,
1670 SIGNOPAQUE)) == -1) {
1673 mutt_unlink (tmpfname);
1674 if (s->flags & M_DISPLAY)
1675 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1680 if (type & ENCRYPT) {
1681 if (!smime_valid_passphrase ())
1682 smime_void_passphrase ();
1683 fputs (SmimePass, smimein);
1684 fputc ('\n', smimein);
1689 mutt_wait_filter (thepid);
1690 mutt_unlink (tmpfname);
1693 if (s->flags & M_DISPLAY) {
1696 if ((c = fgetc (smimeerr)) != EOF) {
1697 ungetc (c, smimeerr);
1699 crypt_current_time (s, "OpenSSL");
1700 mutt_copy_stream (smimeerr, s->fpout);
1701 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1705 state_attach_puts (_("[-- The following data is S/MIME"
1706 " encrypted --]\n"), s);
1708 state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1719 mutt_mktemp (tmptmpfname);
1720 if ((fpout = safe_fopen (tmptmpfname, "w+")) == NULL) {
1721 mutt_perror (tmptmpfname);
1727 while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1728 len = mutt_strlen (buf);
1729 if (len > 1 && buf[len - 2] == '\r') {
1730 buf[len - 2] = '\n';
1731 buf[len - 1] = '\0';
1739 if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1740 fstat (fileno (fpout), &info);
1741 p->length = info.st_size - p->offset;
1743 mutt_parse_part (fpout, p);
1746 tmpfp_buffer = s->fpin;
1748 mutt_body_handler (p, s);
1749 s->fpin = tmpfp_buffer;
1755 mutt_unlink (outfile);
1759 mutt_unlink (tmptmpfname);
1764 if (s->flags & M_DISPLAY) {
1766 state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1768 state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1771 if (type & SIGNOPAQUE) {
1778 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1779 if (linelen && !safe_strcasecmp (line, "verification successful"))
1784 m->goodsig = p->goodsig;
1785 m->badsig = p->badsig;
1796 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1800 char tempfile[_POSIX_PATH_MAX];
1802 long tmpoffset = b->offset;
1803 size_t tmplength = b->length;
1804 int origType = b->type;
1808 if (!mutt_is_application_smime (b))
1814 memset (&s, 0, sizeof (s));
1816 fseek (s.fpin, b->offset, 0);
1818 mutt_mktemp (tempfile);
1819 if ((tmpfp = safe_fopen (tempfile, "w+")) == NULL) {
1820 mutt_perror (tempfile);
1824 mutt_unlink (tempfile);
1826 mutt_decode_attachment (b, &s);
1828 b->length = ftell (s.fpout);
1834 mutt_mktemp (tempfile);
1835 if ((*fpout = safe_fopen (tempfile, "w+")) == NULL) {
1836 mutt_perror (tempfile);
1840 mutt_unlink (tempfile);
1842 if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1847 (*cur)->goodsig = b->goodsig;
1848 (*cur)->badsig = b->badsig;
1852 b->length = tmplength;
1853 b->offset = tmpoffset;
1855 safe_fclose (&tmpfp);
1862 void smime_application_smime_handler (BODY * m, STATE * s)
1865 smime_handle_entity (m, s, NULL);
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 */
1886 msg->security |= ENCRYPT;
1887 switch (mutt_multi_choice (_("1: DES, 2: Triple-DES, 3: RC2-40,"
1888 " 4: RC2-64, 5: RC2-128, or (f)orget it? "),
1891 str_replace (&SmimeCryptAlg, "des");
1894 str_replace (&SmimeCryptAlg, "des3");
1897 str_replace (&SmimeCryptAlg, "rc2-40");
1900 str_replace (&SmimeCryptAlg, "rc2-64");
1903 str_replace (&SmimeCryptAlg, "rc2-128");
1905 case 6: /* forget it */
1910 case 2: /* (s)ign */
1912 if (!SmimeDefaultKey)
1913 mutt_message (_("Can't sign: No key specified. Use Sign As."));
1916 msg->security |= SIGN;
1917 msg->security &= ~ENCRYPT;
1921 case 4: /* sign (a)s */
1923 if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1924 p[mutt_strlen (p) - 1] = '\0';
1925 str_replace (&SmimeDefaultKey, p);
1927 msg->security |= SIGN;
1929 /* probably need a different passphrase */
1930 crypt_smime_void_passphrase ();
1934 msg->security &= ~SIGN;
1937 *redraw = REDRAW_FULL;
1940 case 5: /* (b)oth */
1941 msg->security |= (ENCRYPT | SIGN);
1944 case 6: /* (f)orget it */
1945 case 7: /* (c)lear */
1950 if (msg->security && msg->security != APPLICATION_SMIME)
1951 msg->security |= APPLICATION_SMIME;
1955 return (msg->security);
1959 #endif /* CRYPT_BACKEND_CLASSIC_SMIME */