2 * Copyright (C) 2001,2002 Oliver Ehli <elmy@acm.org>
3 * Copyright (C) 2002 Mike Schiraldi <raldi@research.netsol.com>
4 * Copyright (C) 2004 g10 Code GmbH
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
26 #include "mutt_curses.h"
27 #include "mutt_menu.h"
44 #ifdef HAVE_SYS_TIME_H
45 # include <sys/time.h>
48 #ifdef HAVE_SYS_RESOURCE_H
49 # include <sys/resource.h>
52 #ifdef CRYPT_BACKEND_CLASSIC_SMIME
54 #include "mutt_crypt.h"
56 struct smime_command_context {
57 const char *key; /* %k */
58 const char *cryptalg; /* %a */
59 const char *fname; /* %f */
60 const char *sig_fname; /* %s */
61 const char *certificates; /* %c */
62 const char *intermediates; /* %i */
71 char trust; /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
72 short public; /* 1=public 0=private */
76 char SmimePass[STRING];
77 time_t SmimeExptime = 0; /* when does the cached passphrase expire? */
80 static char SmimeKeyToUse[_POSIX_PATH_MAX] = { 0 };
81 static char SmimeCertToUse[_POSIX_PATH_MAX];
82 static char SmimeIntermediateToUse[_POSIX_PATH_MAX];
86 * Queries and passphrase handling.
92 /* these are copies from pgp.c */
95 void smime_void_passphrase (void)
97 memset (SmimePass, 0, sizeof (SmimePass));
101 int smime_valid_passphrase (void)
103 time_t now = time (NULL);
105 if (now < SmimeExptime)
106 /* Use cached copy. */
109 smime_void_passphrase ();
111 if (mutt_get_password
112 (_("Enter SMIME passphrase:"), SmimePass, sizeof (SmimePass)) == 0) {
113 SmimeExptime = time (NULL) + SmimeTimeout;
124 * The OpenSSL interface
127 /* This is almost identical to ppgp's invoking interface. */
129 static const char *_mutt_fmt_smime_command (char *dest,
134 const char *ifstring,
135 const char *elsestring,
140 struct smime_command_context *cctx = (struct smime_command_context *) data;
141 int optional = (flags & M_FORMAT_OPTIONAL);
147 char path[_POSIX_PATH_MAX];
148 char buf1[LONG_STRING], buf2[LONG_STRING];
151 strfcpy (path, NONULL (SmimeCALocation), sizeof (path));
152 mutt_expand_path (path, sizeof (path));
153 mutt_quote_filename (buf1, sizeof (buf1), path);
155 if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
156 snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
158 snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
160 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
161 snprintf (dest, destlen, fmt, buf2);
163 else if (!SmimeCALocation)
169 { /* certificate (list) */
171 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
172 snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
174 else if (!cctx->certificates)
180 { /* intermediate certificates */
182 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
183 snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
185 else if (!cctx->intermediates)
191 { /* detached signature */
193 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
194 snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
196 else if (!cctx->sig_fname)
204 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
205 snprintf (dest, destlen, fmt, NONULL (cctx->key));
213 { /* algorithm for encryption */
215 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
216 snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
224 { /* file to process */
226 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
227 snprintf (dest, destlen, fmt, NONULL (cctx->fname));
229 else if (!cctx->fname)
240 mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
242 else if (flags & M_FORMAT_OPTIONAL)
243 mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
251 static void mutt_smime_command (char *d, size_t dlen,
252 struct smime_command_context *cctx,
255 mutt_FormatString (d, dlen, NONULL (fmt), _mutt_fmt_smime_command,
256 (unsigned long) cctx, 0);
257 dprint (2, (debugfile, "mutt_smime_command: %s\n", d));
263 static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
264 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
265 int smimeerrfd, const char *fname,
266 const char *sig_fname, const char *cryptalg,
267 const char *key, const char *certificates,
268 const char *intermediates, const char *format)
270 struct smime_command_context cctx;
271 char cmd[HUGE_STRING];
273 memset (&cctx, 0, sizeof (cctx));
275 if (!format || !*format)
279 cctx.sig_fname = sig_fname;
281 cctx.cryptalg = cryptalg;
282 cctx.certificates = certificates;
283 cctx.intermediates = intermediates;
285 mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
287 return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
288 smimeinfd, smimeoutfd, smimeerrfd);
297 * Key and certificate handling.
303 Search the certificate index for given mailbox.
304 return certificate file name.
307 static void smime_entry (char *s, size_t l, MUTTMENU * menu, int num)
309 smime_id *Table = (smime_id *) menu->data;
310 smime_id this = Table[num];
313 switch (this.trust) {
315 truststate = N_("Trusted ");
318 truststate = N_("Verified ");
321 truststate = N_("Unverified");
324 truststate = N_("Expired ");
327 truststate = N_("Revoked ");
330 truststate = N_("Invalid ");
333 truststate = N_("Unknown ");
336 snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
337 truststate, this.email, this.nick);
339 snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
340 this.email, this.nick);
347 char *smime_ask_for_key (char *prompt, char *mailbox, short public)
351 long cert_num; /* Will contain the number of certificates.
352 * To be able to get it, the .index file will be read twice... */
353 char index_file[_POSIX_PATH_MAX];
355 char buf[LONG_STRING];
356 char fields[5][STRING];
357 int numFields, hash_suffix, done, cur; /* The current entry */
360 char helpstr[HUGE_STRING * 3];
365 prompt = _("Enter keyID: ");
366 snprintf (index_file, sizeof (index_file), "%s/.index",
367 public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));
369 index = fopen (index_file, "r");
371 mutt_perror (index_file);
376 while (!feof (index)) {
377 if (fgets (buf, sizeof (buf), index))
384 if (mutt_get_field (prompt, qry, sizeof (qry), 0))
386 snprintf (title, sizeof (title),
387 _("S/MIME certificates matching \"%s\"."), qry);
390 index = fopen (index_file, "r");
392 mutt_perror (index_file);
397 Table = safe_calloc (cert_num, sizeof (smime_id));
398 while (!feof (index)) {
400 fscanf (index, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
401 fields[0], &hash, &hash_suffix, fields[2]);
403 fscanf (index, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
404 fields[3], fields[4]);
406 /* 0=email 1=name 2=nick 3=intermediate 4=trust */
410 /* Check if query matches this certificate */
411 if (!mutt_stristr (fields[0], qry) && !mutt_stristr (fields[2], qry))
414 Table[cur].hash = hash;
415 Table[cur].suffix = hash_suffix;
416 strncpy (Table[cur].email, fields[0], sizeof (Table[cur].email));
417 strncpy (Table[cur].nick, fields[2], sizeof (Table[cur].nick));
418 Table[cur].trust = *fields[4];
419 Table[cur].public = public;
425 /* Make Helpstring */
427 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT);
428 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
429 mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME,
430 OP_GENERIC_SELECT_ENTRY);
431 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
432 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
433 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
435 /* Create the menu */
436 menu = mutt_new_menu ();
438 menu->make_entry = smime_entry;
439 menu->menu = MENU_SMIME;
440 menu->help = helpstr;
443 /* sorting keys might be done later - TODO */
450 switch (mutt_menuLoop (menu)) {
451 case OP_GENERIC_SELECT_ENTRY:
463 fname = safe_malloc (13); /* Hash + '.' + Suffix + \0 */
464 sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
469 mutt_menuDestroy (&menu);
471 set_option (OPTNEEDREDRAW);
480 char *smime_get_field_from_db (char *mailbox, char *query, short public,
483 int addr_len, query_len, found = 0, ask = 0, choice = 0;
484 char cert_path[_POSIX_PATH_MAX];
485 char buf[LONG_STRING], prompt[STRING];
486 char fields[5][STRING];
490 char key_trust_level = 0;
493 if (!mailbox && !query)
496 addr_len = mailbox ? mutt_strlen (mailbox) : 0;
497 query_len = query ? mutt_strlen (query) : 0;
501 /* index-file format:
502 mailbox certfile label issuer_certfile trust_flags\n
504 certfile is a hash value generated by openssl.
505 Note that this was done according to the OpenSSL
506 specs on their CA-directory.
509 snprintf (cert_path, sizeof (cert_path), "%s/.index",
510 (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));
512 if (!stat (cert_path, &info)) {
513 if ((fp = safe_fopen (cert_path, "r")) == NULL) {
514 mutt_perror (cert_path);
518 while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
519 if (mailbox && !(mutt_strncasecmp (mailbox, buf, addr_len))) {
520 numFields = sscanf (buf,
521 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
522 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
523 MUTT_FORMAT (STRING) "\n",
524 fields[0], fields[1],
525 fields[2], fields[3], fields[4]);
528 if (mailbox && public &&
530 *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
534 if (public && *fields[4] == 'u')
535 snprintf (prompt, sizeof (prompt),
537 ("ID %s is unverified. Do you want to use it for %s ?"),
539 else if (public && *fields[4] == 'v')
540 snprintf (prompt, sizeof (prompt),
541 _("Use (untrusted!) ID %s for %s ?"),
544 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
548 if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
554 else if (choice == M_NO) {
558 else if (choice == M_YES) {
559 strfcpy (key, fields[1], sizeof (key));
566 key_trust_level = *fields[4];
567 strfcpy (key, fields[1], sizeof (key));
572 numFields = sscanf (buf,
573 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
574 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
575 MUTT_FORMAT (STRING) "\n",
576 fields[0], fields[1],
577 fields[2], fields[3], fields[4]);
579 /* query = label: return certificate. */
580 if (numFields >= 3 &&
581 !(mutt_strncasecmp (query, fields[2], query_len))) {
583 strfcpy (key, fields[1], sizeof (key));
585 /* query = certificate: return intermediate certificate. */
586 else if (numFields >= 4 &&
587 !(mutt_strncasecmp (query, fields[1], query_len))) {
589 strfcpy (key, fields[3], sizeof (key));
596 if (public && *fields[4] == 'u')
597 snprintf (prompt, sizeof (prompt),
598 _("ID %s is unverified. Do you want to use it for %s ?"),
600 else if (public && *fields[4] == 'v')
601 snprintf (prompt, sizeof (prompt),
602 _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
604 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
606 choice = mutt_yesorno (prompt, M_NO);
607 if (choice == -1 || choice == M_NO)
610 else if (key_trust_level && may_ask) {
611 if (key_trust_level == 'u') {
612 snprintf (prompt, sizeof (prompt),
613 _("ID %s is unverified. Do you want to use it for %s ?"),
615 choice = mutt_yesorno (prompt, M_NO);
619 else if (key_trust_level == 'v') {
621 ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
629 /* Note: safe_strdup ("") returns NULL. */
630 return safe_strdup (key);
637 This sets the '*ToUse' variables for an upcoming decryption, where
638 the reuquired key is different from SmimeDefaultKey.
641 void _smime_getkeys (char *mailbox)
646 k = smime_get_field_from_db (mailbox, NULL, 0, 1);
649 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
650 k = smime_ask_for_key (buf, mailbox, 0);
654 /* the key used last time. */
655 if (*SmimeKeyToUse &&
656 !mutt_strcasecmp (k, SmimeKeyToUse + mutt_strlen (SmimeKeys) + 1)) {
661 smime_void_passphrase ();
663 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
664 NONULL (SmimeKeys), k);
666 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
667 NONULL (SmimeCertificates), k);
669 if (mutt_strcasecmp (k, SmimeDefaultKey))
670 smime_void_passphrase ();
676 if (*SmimeKeyToUse) {
677 if (!mutt_strcasecmp (SmimeDefaultKey,
678 SmimeKeyToUse + mutt_strlen (SmimeKeys) + 1))
681 smime_void_passphrase ();
684 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
685 NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
687 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
688 NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
691 void smime_getkeys (ENVELOPE * env)
696 if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
697 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
698 NONULL (SmimeKeys), SmimeDefaultKey);
700 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
701 NONULL (SmimeCertificates), SmimeDefaultKey);
706 for (t = env->to; !found && t; t = t->next)
707 if (mutt_addr_is_user (t)) {
709 _smime_getkeys (t->mailbox);
711 for (t = env->cc; !found && t; t = t->next)
712 if (mutt_addr_is_user (t)) {
714 _smime_getkeys (t->mailbox);
716 if (!found && (t = mutt_default_from ())) {
717 _smime_getkeys (t->mailbox);
718 rfc822_free_address (&t);
722 /* This routine attempts to find the keyids of the recipients of a message.
723 * It returns NULL if any of the keys can not be found.
726 char *smime_findKeys (ADDRESS * to, ADDRESS * cc, ADDRESS * bcc)
728 char *keyID, *keylist = NULL;
729 size_t keylist_size = 0;
730 size_t keylist_used = 0;
731 ADDRESS *tmp = NULL, *addr = NULL;
732 ADDRESS **last = &tmp;
736 const char *fqdn = mutt_fqdn (1);
738 for (i = 0; i < 3; i++) {
753 *last = rfc822_cpy_adr (p);
755 last = &((*last)->next);
759 rfc822_qualify (tmp, fqdn);
761 tmp = mutt_remove_duplicates (tmp);
763 for (p = tmp; p; p = p->next) {
764 char buf[LONG_STRING];
768 if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
769 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
770 keyID = smime_ask_for_key (buf, q->mailbox, 1);
773 mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
775 rfc822_free_address (&tmp);
776 rfc822_free_address (&addr);
780 keylist_size += mutt_strlen (keyID) + 2;
781 safe_realloc (&keylist, keylist_size);
782 sprintf (keylist + keylist_used, "%s\n", keyID); /* __SPRINTF_CHECKED__ */
783 keylist_used = mutt_strlen (keylist);
785 rfc822_free_address (&addr);
788 rfc822_free_address (&tmp);
797 static int smime_handle_cert_email (char *certificate, char *mailbox,
798 int copy, char ***buffer, int *num)
800 FILE *fpout = NULL, *fperr = NULL;
801 char tmpfname[_POSIX_PATH_MAX];
803 int ret = -1, count = 0;
806 mutt_mktemp (tmpfname);
807 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
808 mutt_perror (tmpfname);
811 mutt_unlink (tmpfname);
813 mutt_mktemp (tmpfname);
814 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
816 mutt_perror (tmpfname);
819 mutt_unlink (tmpfname);
821 if ((thepid = smime_invoke (NULL, NULL, NULL,
822 -1, fileno (fpout), fileno (fperr),
823 certificate, NULL, NULL, NULL, NULL, NULL,
824 SmimeGetCertEmailCommand)) == -1) {
825 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
831 mutt_wait_filter (thepid);
839 while ((fgets (email, sizeof (email), fpout))) {
840 *(email + mutt_strlen (email) - 1) = '\0';
841 if (mutt_strncasecmp (email, mailbox, mutt_strlen (mailbox)) == 0)
844 ret = ret < 0 ? 0 : ret;
850 mutt_copy_stream (fperr, stdout);
851 mutt_any_key_to_continue (_
852 ("Error: unable to create OpenSSL subprocess!"));
860 if (copy && buffer && num) {
862 *buffer = safe_calloc (sizeof (char *), count);
866 while ((fgets (email, sizeof (email), fpout))) {
867 *(email + mutt_strlen (email) - 1) = '\0';
868 (*buffer)[count] = safe_calloc (1, mutt_strlen (email) + 1);
869 strncpy ((*buffer)[count], email, mutt_strlen (email));
884 static char *smime_extract_certificate (char *infile)
886 FILE *fpout = NULL, *fperr = NULL;
887 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
888 char tmpfname[_POSIX_PATH_MAX];
893 mutt_mktemp (tmpfname);
894 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
895 mutt_perror (tmpfname);
898 mutt_unlink (tmpfname);
900 mutt_mktemp (pk7out);
901 if ((fpout = safe_fopen (pk7out, "w+")) == NULL) {
903 mutt_perror (pk7out);
907 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
908 extract the full set of certificates directly.
910 if ((thepid = smime_invoke (NULL, NULL, NULL,
911 -1, fileno (fpout), fileno (fperr),
912 infile, NULL, NULL, NULL, NULL, NULL,
913 SmimePk7outCommand)) == -1) {
914 mutt_any_key_to_continue (_
915 ("Error: unable to create OpenSSL subprocess!"));
918 mutt_unlink (pk7out);
922 mutt_wait_filter (thepid);
929 empty = (fgetc (fpout) == EOF);
931 mutt_perror (pk7out);
932 mutt_copy_stream (fperr, stdout);
935 mutt_unlink (pk7out);
942 mutt_mktemp (certfile);
943 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
945 mutt_unlink (pk7out);
946 mutt_perror (certfile);
950 /* Step 2: Extract the certificates from a PKCS#7 structure.
952 if ((thepid = smime_invoke (NULL, NULL, NULL,
953 -1, fileno (fpout), fileno (fperr),
954 pk7out, NULL, NULL, NULL, NULL, NULL,
955 SmimeGetCertCommand)) == -1) {
956 mutt_any_key_to_continue (_
957 ("Error: unable to create OpenSSL subprocess!"));
960 mutt_unlink (pk7out);
961 mutt_unlink (certfile);
965 mutt_wait_filter (thepid);
967 mutt_unlink (pk7out);
973 empty = (fgetc (fpout) == EOF);
975 mutt_copy_stream (fperr, stdout);
978 mutt_unlink (certfile);
985 return safe_strdup (certfile);
988 static char *smime_extract_signer_certificate (char *infile)
990 FILE *fpout = NULL, *fperr = NULL;
991 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
992 char tmpfname[_POSIX_PATH_MAX];
997 mutt_mktemp (tmpfname);
998 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
999 mutt_perror (tmpfname);
1002 mutt_unlink (tmpfname);
1005 mutt_mktemp (certfile);
1006 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
1008 mutt_perror (certfile);
1012 /* Extract signer's certificate
1014 if ((thepid = smime_invoke (NULL, NULL, NULL,
1015 -1, -1, fileno (fperr),
1016 infile, NULL, NULL, NULL, certfile, NULL,
1017 SmimeGetSignerCertCommand)) == -1) {
1018 mutt_any_key_to_continue (_
1019 ("Error: unable to create OpenSSL subprocess!"));
1022 mutt_unlink (pk7out);
1023 mutt_unlink (certfile);
1027 mutt_wait_filter (thepid);
1033 empty = (fgetc (fpout) == EOF);
1036 mutt_copy_stream (fperr, stdout);
1037 mutt_any_key_to_continue (NULL);
1040 mutt_unlink (certfile);
1047 return safe_strdup (certfile);
1053 /* Add a certificate and update index file (externally). */
1055 void smime_invoke_import (char *infile, char *mailbox)
1057 char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1058 FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1061 mutt_mktemp (tmpfname);
1062 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1063 mutt_perror (tmpfname);
1066 mutt_unlink (tmpfname);
1068 mutt_mktemp (tmpfname);
1069 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
1071 mutt_perror (tmpfname);
1074 mutt_unlink (tmpfname);
1078 if (option (OPTASKCERTLABEL))
1079 mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1082 if ((certfile = smime_extract_certificate (infile))) {
1085 if ((thepid = smime_invoke (&smimein, NULL, NULL,
1086 -1, fileno (fpout), fileno (fperr),
1087 certfile, NULL, NULL, NULL, NULL, NULL,
1088 SmimeImportCertCommand)) == -1) {
1089 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1092 fputs (buf, smimein);
1093 fputc ('\n', smimein);
1096 mutt_wait_filter (thepid);
1098 mutt_unlink (certfile);
1107 mutt_copy_stream (fpout, stdout);
1108 mutt_copy_stream (fperr, stdout);
1117 int smime_verify_sender (HEADER * h)
1119 char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1123 mutt_mktemp (tempfname);
1124 if (!(fpout = safe_fopen (tempfname, "w"))) {
1125 mutt_perror (tempfname);
1129 if (h->security & ENCRYPT)
1130 mutt_copy_message (fpout, Context, h,
1131 M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1132 CH_MIME | CH_WEED | CH_NONEWLINE);
1134 mutt_copy_message (fpout, Context, h, 0, 0);
1140 h->env->from = mutt_expand_aliases (h->env->from);
1141 mbox = h->env->from->mailbox;
1143 else if (h->env->sender) {
1144 h->env->sender = mutt_expand_aliases (h->env->sender);
1145 mbox = h->env->sender->mailbox;
1149 if ((certfile = smime_extract_signer_certificate (tempfname))) {
1150 mutt_unlink (tempfname);
1151 if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1153 mutt_any_key_to_continue (NULL);
1157 mutt_unlink (certfile);
1161 mutt_any_key_to_continue (_("no certfile"));
1164 mutt_any_key_to_continue (_("no mbox"));
1166 mutt_unlink (tempfname);
1179 * Creating S/MIME - bodies.
1186 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1187 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1188 int smimeerrfd, const char *fname,
1191 return smime_invoke (smimein, smimeout, smimeerr,
1192 smimeinfd, smimeoutfd, smimeerrfd,
1193 fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1194 SmimeEncryptCommand);
1199 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1200 int smimeinfd, int smimeoutfd, int smimeerrfd,
1203 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1204 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1205 SmimeCertToUse, SmimeIntermediateToUse,
1212 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1214 char buf[LONG_STRING], certfile[LONG_STRING];
1215 char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1216 char smimeinfile[_POSIX_PATH_MAX];
1217 char *cert_start = certlist, *cert_end = certlist;
1218 FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1223 mutt_mktemp (tempfile);
1224 if ((fpout = safe_fopen (tempfile, "w+")) == NULL) {
1225 mutt_perror (tempfile);
1229 mutt_mktemp (smimeerrfile);
1230 if ((smimeerr = safe_fopen (smimeerrfile, "w+")) == NULL) {
1231 mutt_perror (smimeerrfile);
1233 mutt_unlink (tempfile);
1236 mutt_unlink (smimeerrfile);
1238 mutt_mktemp (smimeinfile);
1239 if ((fptmp = safe_fopen (smimeinfile, "w+")) == NULL) {
1240 mutt_perror (smimeinfile);
1241 mutt_unlink (tempfile);
1249 int off = mutt_strlen (certfile);
1251 while (*++cert_end && *cert_end != '\n');
1255 snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1256 NONULL (SmimeCertificates), cert_start);
1258 cert_start = cert_end;
1262 /* write a MIME entity */
1263 mutt_write_mime_header (a, fptmp);
1264 fputc ('\n', fptmp);
1265 mutt_write_mime_body (a, fptmp);
1269 smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1270 fileno (fpout), fileno (smimeerr),
1271 smimeinfile, certfile)) == -1) {
1273 mutt_unlink (smimeinfile);
1274 mutt_unlink (certfile);
1280 mutt_wait_filter (thepid);
1281 mutt_unlink (smimeinfile);
1282 mutt_unlink (certfile);
1286 empty = (fgetc (fpout) == EOF);
1291 while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1293 fputs (buf, stdout);
1297 /* pause if there is any error output from SMIME */
1299 mutt_any_key_to_continue (NULL);
1302 /* fatal error while trying to encrypt message */
1304 mutt_any_key_to_continue _("No output from OpenSSL..");
1306 mutt_unlink (tempfile);
1310 t = mutt_new_body ();
1311 t->type = TYPEAPPLICATION;
1312 t->subtype = safe_strdup ("x-pkcs7-mime");
1313 mutt_set_parameter ("name", "smime.p7m", &t->parameter);
1314 mutt_set_parameter ("smime-type", "enveloped-data", &t->parameter);
1315 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
1317 t->disposition = DISPATTACH;
1318 t->d_filename = safe_strdup ("smime.p7m");
1319 t->filename = safe_strdup (tempfile);
1320 t->unlink = 1; /*delete after sending the message */
1330 BODY *smime_sign_message (BODY * a)
1333 char buffer[LONG_STRING];
1334 char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1335 FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1339 char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1341 if (!intermediates) {
1342 mutt_message (_("Warning: Intermediate certificate not found."));
1343 intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
1346 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
1348 mutt_mktemp (filetosign);
1349 if ((sfp = safe_fopen (filetosign, "w+")) == NULL) {
1350 mutt_perror (filetosign);
1354 mutt_mktemp (signedfile);
1355 if ((smimeout = safe_fopen (signedfile, "w+")) == NULL) {
1356 mutt_perror (signedfile);
1358 mutt_unlink (filetosign);
1362 mutt_write_mime_header (a, sfp);
1364 mutt_write_mime_body (a, sfp);
1369 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1370 NONULL (SmimeKeys), SmimeDefaultKey);
1372 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1373 NONULL (SmimeCertificates), SmimeDefaultKey);
1375 snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1376 NONULL (SmimeCertificates), intermediates);
1380 if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1381 -1, fileno (smimeout), -1,
1382 filetosign)) == -1) {
1383 mutt_perror _("Can't open OpenSSL subprocess!");
1386 mutt_unlink (signedfile);
1387 mutt_unlink (filetosign);
1390 fputs (SmimePass, smimein);
1391 fputc ('\n', smimein);
1395 mutt_wait_filter (thepid);
1397 /* check for errors from OpenSSL */
1401 while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1403 fputs (buffer, stdout);
1410 empty = (fgetc (smimeout) == EOF);
1413 mutt_unlink (filetosign);
1417 mutt_any_key_to_continue (NULL);
1420 mutt_any_key_to_continue _("No output from OpenSSL...");
1422 mutt_unlink (signedfile);
1423 return (NULL); /* fatal error while signing */
1426 t = mutt_new_body ();
1427 t->type = TYPEMULTIPART;
1428 t->subtype = safe_strdup ("signed");
1429 t->encoding = ENC7BIT;
1431 t->disposition = DISPINLINE;
1433 mutt_generate_boundary (&t->parameter);
1434 /* check if this can be extracted from private key somehow.... */
1435 mutt_set_parameter ("micalg", "sha1", &t->parameter);
1436 mutt_set_parameter ("protocol", "application/x-pkcs7-signature",
1442 t->parts->next = mutt_new_body ();
1444 t->type = TYPEAPPLICATION;
1445 t->subtype = safe_strdup ("x-pkcs7-signature");
1446 t->filename = safe_strdup (signedfile);
1447 t->d_filename = safe_strdup ("smime.p7s");
1449 t->disposition = DISPATTACH;
1450 t->encoding = ENCBASE64;
1451 t->unlink = 1; /* ok to remove this file after sending. */
1463 * Handling S/MIME - bodies.
1472 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1473 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1474 int smimeerrfd, const char *fname,
1475 const char *sig_fname, int opaque)
1477 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1478 smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1479 (opaque ? SmimeVerifyOpaqueCommand :
1480 SmimeVerifyCommand));
1485 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1486 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1487 int smimeerrfd, const char *fname)
1489 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1490 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1491 SmimeCertToUse, NULL, SmimeDecryptCommand);
1496 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1498 char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1499 FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1504 size_t tmplength = 0;
1505 int origType = sigbdy->type;
1506 char *savePrefix = NULL;
1509 snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1511 /* decode to a tempfile, saving the original destination */
1513 if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1514 mutt_perror (signedfile);
1517 /* decoding the attachment changes the size and offset, so save a copy
1518 * of the "real" values now, and restore them after processing
1520 tmplength = sigbdy->length;
1521 tmpoffset = sigbdy->offset;
1523 /* if we are decoding binary bodies, we don't want to prefix each
1524 * line with the prefix or else the data will get corrupted.
1526 savePrefix = s->prefix;
1529 mutt_decode_attachment (sigbdy, s);
1531 sigbdy->length = ftell (s->fpout);
1535 /* restore final destination and substitute the tempfile for input */
1538 s->fpin = fopen (signedfile, "r");
1540 /* restore the prefix */
1541 s->prefix = savePrefix;
1543 sigbdy->type = origType;
1546 mutt_mktemp (smimeerrfile);
1547 if (!(smimeerr = safe_fopen (smimeerrfile, "w+"))) {
1548 mutt_perror (smimeerrfile);
1549 mutt_unlink (signedfile);
1553 crypt_current_time (s, "OpenSSL");
1555 if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1556 -1, -1, fileno (smimeerr),
1557 tempfile, signedfile, 0)) != -1) {
1561 if (mutt_wait_filter (thepid))
1571 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1572 if (linelen && !mutt_strcasecmp (line, "verification successful"))
1581 mutt_copy_stream (smimeerr, s->fpout);
1584 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1586 mutt_unlink (signedfile);
1587 mutt_unlink (smimeerrfile);
1589 sigbdy->length = tmplength;
1590 sigbdy->offset = tmpoffset;
1592 /* restore the original source stream */
1605 This handles application/pkcs7-mime which can either be a signed
1606 or an encrypted message.
1609 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1614 char buf[HUGE_STRING];
1615 char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1616 char tmpfname[_POSIX_PATH_MAX];
1617 char tmptmpfname[_POSIX_PATH_MAX];
1618 FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1619 FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1623 unsigned int type = mutt_is_application_smime (m);
1625 if (!(type & APPLICATION_SMIME))
1628 mutt_mktemp (outfile);
1629 if ((smimeout = safe_fopen (outfile, "w+")) == NULL) {
1630 mutt_perror (outfile);
1634 mutt_mktemp (errfile);
1635 if ((smimeerr = safe_fopen (errfile, "w+")) == NULL) {
1636 mutt_perror (errfile);
1641 mutt_unlink (errfile);
1644 mutt_mktemp (tmpfname);
1645 if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL) {
1646 mutt_perror (tmpfname);
1654 fseek (s->fpin, m->offset, 0);
1655 last_pos = m->offset;
1657 mutt_copy_bytes (s->fpin, tmpfp, m->length);
1662 if ((type & ENCRYPT) &&
1663 (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1664 fileno (smimeout), fileno (smimeerr),
1668 mutt_unlink (tmpfname);
1669 state_attach_puts (_
1670 ("[-- Error: unable to create OpenSSL subprocess! --]\n"),
1674 else if ((type & SIGNOPAQUE) &&
1675 (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1677 fileno (smimeerr), NULL, tmpfname,
1678 SIGNOPAQUE)) == -1) {
1681 mutt_unlink (tmpfname);
1682 state_attach_puts (_
1683 ("[-- Error: unable to create OpenSSL subprocess! --]\n"),
1689 if (type & ENCRYPT) {
1690 if (!smime_valid_passphrase ())
1691 smime_void_passphrase ();
1692 fputs (SmimePass, smimein);
1693 fputc ('\n', smimein);
1698 mutt_wait_filter (thepid);
1699 mutt_unlink (tmpfname);
1702 if (s->flags & M_DISPLAY) {
1705 if ((c = fgetc (smimeerr)) != EOF) {
1706 ungetc (c, smimeerr);
1708 crypt_current_time (s, "OpenSSL");
1709 mutt_copy_stream (smimeerr, s->fpout);
1710 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1714 state_attach_puts (_("[-- The following data is S/MIME"
1715 " encrypted --]\n"), s);
1717 state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1728 mutt_mktemp (tmptmpfname);
1729 if ((fpout = safe_fopen (tmptmpfname, "w+")) == NULL) {
1730 mutt_perror (tmptmpfname);
1736 while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1737 len = mutt_strlen (buf);
1738 if (len > 1 && buf[len - 2] == '\r') {
1739 buf[len - 2] = '\n';
1740 buf[len - 1] = '\0';
1748 if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1749 fstat (fileno (fpout), &info);
1750 p->length = info.st_size - p->offset;
1752 mutt_parse_part (fpout, p);
1755 tmpfp_buffer = s->fpin;
1757 mutt_body_handler (p, s);
1758 s->fpin = tmpfp_buffer;
1764 mutt_unlink (outfile);
1768 mutt_unlink (tmptmpfname);
1773 if (s->flags & M_DISPLAY) {
1775 state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1777 state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1780 if (type & SIGNOPAQUE) {
1787 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1788 if (linelen && !mutt_strcasecmp (line, "verification successful"))
1793 m->goodsig = p->goodsig;
1794 m->badsig = p->badsig;
1805 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1809 char tempfile[_POSIX_PATH_MAX];
1811 long tmpoffset = b->offset;
1812 size_t tmplength = b->length;
1813 int origType = b->type;
1816 if (!mutt_is_application_smime (b))
1822 memset (&s, 0, sizeof (s));
1824 fseek (s.fpin, b->offset, 0);
1826 mutt_mktemp (tempfile);
1827 if ((tmpfp = safe_fopen (tempfile, "w+")) == NULL) {
1828 mutt_perror (tempfile);
1832 mutt_unlink (tempfile);
1834 mutt_decode_attachment (b, &s);
1836 b->length = ftell (s.fpout);
1842 mutt_mktemp (tempfile);
1843 if ((*fpout = safe_fopen (tempfile, "w+")) == NULL) {
1844 mutt_perror (tempfile);
1847 mutt_unlink (tempfile);
1849 *cur = smime_handle_entity (b, &s, *fpout);
1850 (*cur)->goodsig = b->goodsig;
1851 (*cur)->badsig = b->badsig;
1853 b->length = tmplength;
1854 b->offset = tmpoffset;
1863 void smime_application_smime_handler (BODY * m, STATE * s)
1866 smime_handle_entity (m, s, NULL);
1870 int smime_send_menu (HEADER * msg, int *redraw)
1874 if (!(WithCrypto & APPLICATION_SMIME))
1875 return msg->security;
1877 switch (mutt_multi_choice
1879 ("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1881 case 1: /* (e)ncrypt */
1882 msg->security |= ENCRYPT;
1883 msg->security &= ~SIGN;
1886 case 3: /* encrypt (w)ith */
1887 msg->security |= ENCRYPT;
1888 switch (mutt_multi_choice (_("1: DES, 2: Triple-DES, 3: RC2-40,"
1889 " 4: RC2-64, 5: RC2-128, or (f)orget it? "),
1892 mutt_str_replace (&SmimeCryptAlg, "des");
1895 mutt_str_replace (&SmimeCryptAlg, "des3");
1898 mutt_str_replace (&SmimeCryptAlg, "rc2-40");
1901 mutt_str_replace (&SmimeCryptAlg, "rc2-64");
1904 mutt_str_replace (&SmimeCryptAlg, "rc2-128");
1906 case 6: /* forget it */
1911 case 2: /* (s)ign */
1913 if (!SmimeDefaultKey)
1914 mutt_message (_("Can't sign: No key specified. Use Sign As."));
1917 msg->security |= SIGN;
1918 msg->security &= ~ENCRYPT;
1922 case 4: /* sign (a)s */
1924 if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1925 p[mutt_strlen (p) - 1] = '\0';
1926 mutt_str_replace (&SmimeDefaultKey, p);
1928 msg->security |= SIGN;
1930 /* probably need a different passphrase */
1931 crypt_smime_void_passphrase ();
1935 msg->security &= ~SIGN;
1938 *redraw = REDRAW_FULL;
1941 case 5: /* (b)oth */
1942 msg->security |= (ENCRYPT | SIGN);
1945 case 6: /* (f)orget it */
1946 case 7: /* (c)lear */
1951 if (msg->security && msg->security != APPLICATION_SMIME)
1952 msg->security |= APPLICATION_SMIME;
1956 return (msg->security);
1960 #endif /* CRYPT_BACKEND_CLASSIC_SMIME */