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/str.h>
18 #include <lib-lib/macros.h>
19 #include <lib-lib/file.h>
21 #include <lib-mime/mime.h>
26 #include "mutt_curses.h"
27 #include "mutt_menu.h"
30 #include "lib/debug.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 * Create a format string to be used with scanf.
87 * To use it, write, for instance, MUTT_FORMAT(HUGE_STRING).
89 * See K&R 2nd ed, p. 231 for an explanation.
91 #define _MUTT_FORMAT_2(a,b) "%" a b
92 #define _MUTT_FORMAT_1(a, b) _MUTT_FORMAT_2(#a, b)
93 #define MUTT_FORMAT(a) _MUTT_FORMAT_1(a, "s")
97 * Queries and passphrase handling.
101 /* these are copies from pgp.c */
104 void smime_void_passphrase (void)
106 p_clear(SmimePass, sizeof(SmimePass));
110 int smime_valid_passphrase (void)
112 time_t now = time (NULL);
114 if (now < SmimeExptime)
115 /* Use cached copy. */
118 smime_void_passphrase ();
120 if (mutt_get_field_unbuffered (_("Enter S/MIME passphrase:"), SmimePass,
121 sizeof (SmimePass), M_PASS) == 0) {
122 SmimeExptime = time (NULL) + SmimeTimeout;
133 * The OpenSSL interface
136 /* This is almost identical to ppgp's invoking interface. */
138 static const char *_mutt_fmt_smime_command (char *dest,
143 const char *ifstring,
144 const char *elsestring,
149 struct smime_command_context *cctx = (struct smime_command_context *) data;
150 int optional = (flags & M_FORMAT_OPTIONAL);
156 char path[_POSIX_PATH_MAX];
157 char buf1[LONG_STRING], buf2[LONG_STRING];
160 m_strcpy(path, sizeof(path), NONULL(SmimeCALocation));
161 mutt_expand_path (path, sizeof (path));
162 mutt_quote_filename (buf1, sizeof (buf1), path);
164 if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
165 snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
167 snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
169 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
170 snprintf (dest, destlen, fmt, buf2);
172 else if (!SmimeCALocation)
178 { /* certificate (list) */
180 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
181 snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
183 else if (!cctx->certificates)
189 { /* intermediate certificates */
191 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
192 snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
194 else if (!cctx->intermediates)
200 { /* detached signature */
202 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
203 snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
205 else if (!cctx->sig_fname)
213 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
214 snprintf (dest, destlen, fmt, NONULL (cctx->key));
222 { /* algorithm for encryption */
224 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
225 snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
233 { /* file to process */
235 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
236 snprintf (dest, destlen, fmt, NONULL (cctx->fname));
238 else if (!cctx->fname)
249 mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
251 else if (flags & M_FORMAT_OPTIONAL)
252 mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
260 static void mutt_smime_command (char *d, size_t dlen,
261 struct smime_command_context *cctx,
264 mutt_FormatString (d, dlen, NONULL (fmt), _mutt_fmt_smime_command,
265 (unsigned long) cctx, 0);
266 debug_print (2, ("%s\n", d));
269 static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
270 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
271 int smimeerrfd, const char *fname,
272 const char *sig_fname, const char *cryptalg,
273 const char *key, const char *certificates,
274 const char *intermediates, const char *format)
276 struct smime_command_context cctx;
277 char cmd[HUGE_STRING];
281 if (!format || !*format)
285 cctx.sig_fname = sig_fname;
287 cctx.cryptalg = cryptalg;
288 cctx.certificates = certificates;
289 cctx.intermediates = intermediates;
291 mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
293 return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
294 smimeinfd, smimeoutfd, smimeerrfd);
303 * Key and certificate handling.
309 Search the certificate index for given mailbox.
310 return certificate file name.
313 static void smime_entry (char *s, size_t l, MUTTMENU * menu, int num)
315 smime_id *Table = (smime_id *) menu->data;
316 smime_id this = Table[num];
317 const char *truststate;
319 switch (this.trust) {
321 truststate = N_("Trusted ");
324 truststate = N_("Verified ");
327 truststate = N_("Unverified");
330 truststate = N_("Expired ");
333 truststate = N_("Revoked ");
336 truststate = N_("Invalid ");
339 truststate = N_("Unknown ");
342 snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
343 truststate, this.email, this.nick);
345 snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
346 this.email, this.nick);
353 char *smime_ask_for_key (char *prompt, char *mailbox, short public)
357 long cert_num; /* Will contain the number of certificates.
358 * To be able to get it, the .index file will be read twice... */
359 char index_file[_POSIX_PATH_MAX];
361 char buf[LONG_STRING];
362 char fields[5][STRING];
363 int numFields, hash_suffix, done, cur; /* The current entry */
366 char helpstr[HUGE_STRING * 3];
371 prompt = _("Enter keyID: ");
372 snprintf (index_file, sizeof (index_file), "%s/.index",
373 public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));
375 index = fopen (index_file, "r");
377 mutt_perror (index_file);
382 while (!feof (index)) {
383 if (fgets (buf, sizeof (buf), index))
390 if (mutt_get_field (prompt, qry, sizeof (qry), 0))
392 snprintf (title, sizeof (title),
393 _("S/MIME certificates matching \"%s\"."), qry);
396 index = fopen (index_file, "r");
398 mutt_perror (index_file);
403 Table = p_new(smime_id, cert_num);
404 while (!feof (index)) {
406 fscanf (index, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
407 fields[0], &hash, &hash_suffix, fields[2]);
409 fscanf (index, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
410 fields[3], fields[4]);
412 /* 0=email 1=name 2=nick 3=intermediate 4=trust */
416 /* Check if query matches this certificate */
417 if (!str_isstr (fields[0], qry) && !str_isstr (fields[2], qry))
420 Table[cur].hash = hash;
421 Table[cur].suffix = hash_suffix;
422 m_strcpy(Table[cur].email, sizeof(Table[cur].email), fields[0]);
423 m_strcpy(Table[cur].nick, sizeof(Table[cur].nick), fields[2]);
424 Table[cur].trust = *fields[4];
425 Table[cur].public = public;
431 /* Make Helpstring */
433 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT);
434 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
435 mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME,
436 OP_GENERIC_SELECT_ENTRY);
437 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
438 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
439 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
441 /* Create the menu */
442 menu = mutt_new_menu ();
444 menu->make_entry = smime_entry;
445 menu->menu = MENU_SMIME;
446 menu->help = helpstr;
449 /* sorting keys might be done later - TODO */
456 switch (mutt_menuLoop (menu)) {
457 case OP_GENERIC_SELECT_ENTRY:
469 fname = p_new(char, 13); /* Hash + '.' + Suffix + \0 */
470 sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
475 mutt_menuDestroy (&menu);
477 set_option (OPTNEEDREDRAW);
486 char *smime_get_field_from_db (char *mailbox, char *query, short public,
489 int addr_len, query_len, found = 0, ask = 0, choice = 0;
490 char cert_path[_POSIX_PATH_MAX];
491 char buf[LONG_STRING], prompt[STRING];
492 char fields[5][STRING];
496 char key_trust_level = 0;
499 if (!mailbox && !query)
502 addr_len = mailbox ? m_strlen(mailbox) : 0;
503 query_len = query ? m_strlen(query) : 0;
507 /* index-file format:
508 mailbox certfile label issuer_certfile trust_flags\n
510 certfile is a hash value generated by openssl.
511 Note that this was done according to the OpenSSL
512 specs on their CA-directory.
515 snprintf (cert_path, sizeof (cert_path), "%s/.index",
516 (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));
518 if (!stat (cert_path, &info)) {
519 if ((fp = safe_fopen (cert_path, "r")) == NULL) {
520 mutt_perror (cert_path);
524 while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
525 if (mailbox && !(m_strncasecmp(mailbox, buf, addr_len))) {
526 numFields = sscanf (buf,
527 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
528 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
529 MUTT_FORMAT (STRING) "\n",
530 fields[0], fields[1],
531 fields[2], fields[3], fields[4]);
534 if (mailbox && public &&
536 *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
540 if (public && *fields[4] == 'u')
541 snprintf (prompt, sizeof (prompt),
543 ("ID %s is unverified. Do you want to use it for %s ?"),
545 else if (public && *fields[4] == 'v')
546 snprintf (prompt, sizeof (prompt),
547 _("Use (untrusted!) ID %s for %s ?"),
550 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
554 if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
560 else if (choice == M_NO) {
564 else if (choice == M_YES) {
565 m_strcpy(key, sizeof(key), fields[1]);
572 key_trust_level = *fields[4];
573 m_strcpy(key, sizeof(key), fields[1]);
578 numFields = sscanf (buf,
579 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
580 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
581 MUTT_FORMAT (STRING) "\n",
582 fields[0], fields[1],
583 fields[2], fields[3], fields[4]);
585 /* query = label: return certificate. */
586 if (numFields >= 3 &&
587 !(m_strncasecmp(query, fields[2], query_len))) {
589 m_strcpy(key, sizeof(key), fields[1]);
591 /* query = certificate: return intermediate certificate. */
592 else if (numFields >= 4 &&
593 !(m_strncasecmp(query, fields[1], query_len))) {
595 m_strcpy(key, sizeof(key), fields[3]);
602 if (public && *fields[4] == 'u')
603 snprintf (prompt, sizeof (prompt),
604 _("ID %s is unverified. Do you want to use it for %s ?"),
606 else if (public && *fields[4] == 'v')
607 snprintf (prompt, sizeof (prompt),
608 _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
610 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
612 choice = mutt_yesorno (prompt, M_NO);
613 if (choice == -1 || choice == M_NO)
616 else if (key_trust_level && may_ask) {
617 if (key_trust_level == 'u') {
618 snprintf (prompt, sizeof (prompt),
619 _("ID %s is unverified. Do you want to use it for %s ?"),
621 choice = mutt_yesorno (prompt, M_NO);
625 else if (key_trust_level == 'v') {
627 ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
635 /* Note: m_strdup("") returns NULL. */
636 return m_strdup(key);
643 This sets the '*ToUse' variables for an upcoming decryption, where
644 the reuquired key is different from SmimeDefaultKey.
647 void _smime_getkeys (char *mailbox)
652 k = smime_get_field_from_db (mailbox, NULL, 0, 1);
655 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
656 k = smime_ask_for_key (buf, mailbox, 0);
660 /* the key used last time. */
661 if (*SmimeKeyToUse &&
662 !m_strcasecmp(k, SmimeKeyToUse + m_strlen(SmimeKeys) + 1)) {
667 smime_void_passphrase ();
669 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
670 NONULL (SmimeKeys), k);
672 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
673 NONULL (SmimeCertificates), k);
675 if (m_strcasecmp(k, SmimeDefaultKey))
676 smime_void_passphrase ();
682 if (*SmimeKeyToUse) {
683 if (!m_strcasecmp(SmimeDefaultKey,
684 SmimeKeyToUse + m_strlen(SmimeKeys) + 1))
687 smime_void_passphrase ();
690 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
691 NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
693 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
694 NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
697 void smime_getkeys (ENVELOPE * env)
702 if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
703 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
704 NONULL (SmimeKeys), SmimeDefaultKey);
706 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
707 NONULL (SmimeCertificates), SmimeDefaultKey);
712 for (t = env->to; !found && t; t = t->next)
713 if (mutt_addr_is_user (t)) {
715 _smime_getkeys (t->mailbox);
717 for (t = env->cc; !found && t; t = t->next)
718 if (mutt_addr_is_user (t)) {
720 _smime_getkeys (t->mailbox);
722 if (!found && (t = mutt_default_from ())) {
723 _smime_getkeys (t->mailbox);
728 /* This routine attempts to find the keyids of the recipients of a message.
729 * It returns NULL if any of the keys can not be found.
732 char *smime_findKeys (address_t * to, address_t * cc, address_t * bcc)
734 char *keyID, *keylist = NULL;
735 size_t keylist_size = 0;
736 size_t keylist_used = 0;
737 address_t *tmp = NULL, *addr = NULL;
738 address_t **last = &tmp;
742 const char *fqdn = mutt_fqdn (1);
744 for (i = 0; i < 3; i++) {
759 *last = address_list_dup (p);
761 last = &((*last)->next);
765 rfc822_qualify (tmp, fqdn);
767 tmp = mutt_remove_duplicates (tmp);
769 for (p = tmp; p; p = p->next) {
770 char buf[LONG_STRING];
774 if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
775 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
776 keyID = smime_ask_for_key (buf, q->mailbox, 1);
779 mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
781 address_delete (&tmp);
782 address_delete (&addr);
786 keylist_size += m_strlen(keyID) + 2;
787 p_realloc(&keylist, keylist_size);
788 sprintf (keylist + keylist_used, "%s\n", keyID); /* __SPRINTF_CHECKED__ */
789 keylist_used = m_strlen(keylist);
791 address_delete (&addr);
794 address_delete (&tmp);
803 static int smime_handle_cert_email (char *certificate, char *mailbox,
804 int copy, char ***buffer, int *num)
806 FILE *fpout = NULL, *fperr = NULL;
807 char tmpfname[_POSIX_PATH_MAX];
809 int ret = -1, count = 0;
812 mutt_mktemp (tmpfname);
813 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
814 mutt_perror (tmpfname);
817 mutt_unlink (tmpfname);
819 mutt_mktemp (tmpfname);
820 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
822 mutt_perror (tmpfname);
825 mutt_unlink (tmpfname);
827 if ((thepid = smime_invoke (NULL, NULL, NULL,
828 -1, fileno (fpout), fileno (fperr),
829 certificate, NULL, NULL, NULL, NULL, NULL,
830 SmimeGetCertEmailCommand)) == -1) {
831 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
837 mutt_wait_filter (thepid);
845 while ((fgets (email, sizeof (email), fpout))) {
846 *(email + m_strlen(email) - 1) = '\0';
847 if (m_strncasecmp(email, mailbox, m_strlen(mailbox)) == 0)
850 ret = ret < 0 ? 0 : ret;
856 mutt_copy_stream (fperr, stdout);
857 mutt_any_key_to_continue (_
858 ("Error: unable to create OpenSSL subprocess!"));
866 if (copy && buffer && num) {
868 *buffer = p_new(char *, count);
872 while ((fgets (email, sizeof (email), fpout))) {
873 *(email + m_strlen(email) - 1) = '\0';
874 (*buffer)[count] = p_dupstr(email, m_strlen(email));
889 static char *smime_extract_certificate (char *infile)
891 FILE *fpout = NULL, *fperr = NULL;
892 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
893 char tmpfname[_POSIX_PATH_MAX];
898 mutt_mktemp (tmpfname);
899 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
900 mutt_perror (tmpfname);
903 mutt_unlink (tmpfname);
905 mutt_mktemp (pk7out);
906 if ((fpout = safe_fopen (pk7out, "w+")) == NULL) {
908 mutt_perror (pk7out);
912 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
913 extract the full set of certificates directly.
915 if ((thepid = smime_invoke (NULL, NULL, NULL,
916 -1, fileno (fpout), fileno (fperr),
917 infile, NULL, NULL, NULL, NULL, NULL,
918 SmimePk7outCommand)) == -1) {
919 mutt_any_key_to_continue (_
920 ("Error: unable to create OpenSSL subprocess!"));
923 mutt_unlink (pk7out);
927 mutt_wait_filter (thepid);
934 empty = (fgetc (fpout) == EOF);
936 mutt_perror (pk7out);
937 mutt_copy_stream (fperr, stdout);
940 mutt_unlink (pk7out);
947 mutt_mktemp (certfile);
948 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
950 mutt_unlink (pk7out);
951 mutt_perror (certfile);
955 /* Step 2: Extract the certificates from a PKCS#7 structure.
957 if ((thepid = smime_invoke (NULL, NULL, NULL,
958 -1, fileno (fpout), fileno (fperr),
959 pk7out, NULL, NULL, NULL, NULL, NULL,
960 SmimeGetCertCommand)) == -1) {
961 mutt_any_key_to_continue (_
962 ("Error: unable to create OpenSSL subprocess!"));
965 mutt_unlink (pk7out);
966 mutt_unlink (certfile);
970 mutt_wait_filter (thepid);
972 mutt_unlink (pk7out);
978 empty = (fgetc (fpout) == EOF);
980 mutt_copy_stream (fperr, stdout);
983 mutt_unlink (certfile);
990 return m_strdup(certfile);
993 static char *smime_extract_signer_certificate (char *infile)
995 FILE *fpout = NULL, *fperr = NULL;
996 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
997 char tmpfname[_POSIX_PATH_MAX];
1002 mutt_mktemp (tmpfname);
1003 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1004 mutt_perror (tmpfname);
1007 mutt_unlink (tmpfname);
1010 mutt_mktemp (certfile);
1011 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
1013 mutt_perror (certfile);
1017 /* Extract signer's certificate
1019 if ((thepid = smime_invoke (NULL, NULL, NULL,
1020 -1, -1, fileno (fperr),
1021 infile, NULL, NULL, NULL, certfile, NULL,
1022 SmimeGetSignerCertCommand)) == -1) {
1023 mutt_any_key_to_continue (_
1024 ("Error: unable to create OpenSSL subprocess!"));
1027 mutt_unlink (pk7out);
1028 mutt_unlink (certfile);
1032 mutt_wait_filter (thepid);
1038 empty = (fgetc (fpout) == EOF);
1041 mutt_copy_stream (fperr, stdout);
1042 mutt_any_key_to_continue (NULL);
1045 mutt_unlink (certfile);
1052 return m_strdup(certfile);
1058 /* Add a certificate and update index file (externally). */
1060 void smime_invoke_import (char *infile, char *mailbox)
1062 char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1063 FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1066 mutt_mktemp (tmpfname);
1067 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1068 mutt_perror (tmpfname);
1071 mutt_unlink (tmpfname);
1073 mutt_mktemp (tmpfname);
1074 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
1076 mutt_perror (tmpfname);
1079 mutt_unlink (tmpfname);
1083 if (option (OPTASKCERTLABEL))
1084 mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1087 if ((certfile = smime_extract_certificate (infile))) {
1090 if ((thepid = smime_invoke (&smimein, NULL, NULL,
1091 -1, fileno (fpout), fileno (fperr),
1092 certfile, NULL, NULL, NULL, NULL, NULL,
1093 SmimeImportCertCommand)) == -1) {
1094 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1097 fputs (buf, smimein);
1098 fputc ('\n', smimein);
1101 mutt_wait_filter (thepid);
1103 mutt_unlink (certfile);
1104 p_delete(&certfile);
1112 mutt_copy_stream (fpout, stdout);
1113 mutt_copy_stream (fperr, stdout);
1122 int smime_verify_sender (HEADER * h)
1124 char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1128 mutt_mktemp (tempfname);
1129 if (!(fpout = safe_fopen (tempfname, "w"))) {
1130 mutt_perror (tempfname);
1134 if (h->security & ENCRYPT)
1135 mutt_copy_message (fpout, Context, h,
1136 M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1137 CH_MIME | CH_WEED | CH_NONEWLINE);
1139 mutt_copy_message (fpout, Context, h, 0, 0);
1145 h->env->from = mutt_expand_aliases (h->env->from);
1146 mbox = h->env->from->mailbox;
1148 else if (h->env->sender) {
1149 h->env->sender = mutt_expand_aliases (h->env->sender);
1150 mbox = h->env->sender->mailbox;
1154 if ((certfile = smime_extract_signer_certificate (tempfname))) {
1155 mutt_unlink (tempfname);
1156 if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1158 mutt_any_key_to_continue (NULL);
1162 mutt_unlink (certfile);
1163 p_delete(&certfile);
1166 mutt_any_key_to_continue (_("no certfile"));
1169 mutt_any_key_to_continue (_("no mbox"));
1171 mutt_unlink (tempfname);
1184 * Creating S/MIME - bodies.
1191 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1192 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1193 int smimeerrfd, const char *fname,
1196 return smime_invoke (smimein, smimeout, smimeerr,
1197 smimeinfd, smimeoutfd, smimeerrfd,
1198 fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1199 SmimeEncryptCommand);
1204 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1205 int smimeinfd, int smimeoutfd, int smimeerrfd,
1208 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1209 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1210 SmimeCertToUse, SmimeIntermediateToUse,
1217 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1219 char buf[LONG_STRING], certfile[LONG_STRING];
1220 char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1221 char smimeinfile[_POSIX_PATH_MAX];
1222 char *cert_start = certlist, *cert_end = certlist;
1223 FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1228 mutt_mktemp (tempfile);
1229 if ((fpout = safe_fopen (tempfile, "w+")) == NULL) {
1230 mutt_perror (tempfile);
1234 mutt_mktemp (smimeerrfile);
1235 if ((smimeerr = safe_fopen (smimeerrfile, "w+")) == NULL) {
1236 mutt_perror (smimeerrfile);
1238 mutt_unlink (tempfile);
1241 mutt_unlink (smimeerrfile);
1243 mutt_mktemp (smimeinfile);
1244 if ((fptmp = safe_fopen (smimeinfile, "w+")) == NULL) {
1245 mutt_perror (smimeinfile);
1246 mutt_unlink (tempfile);
1254 int off = m_strlen(certfile);
1256 while (*++cert_end && *cert_end != '\n');
1260 snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1261 NONULL (SmimeCertificates), cert_start);
1263 cert_start = cert_end;
1267 /* write a MIME entity */
1268 mutt_write_mime_header (a, fptmp);
1269 fputc ('\n', fptmp);
1270 mutt_write_mime_body (a, fptmp);
1274 smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1275 fileno (fpout), fileno (smimeerr),
1276 smimeinfile, certfile)) == -1) {
1278 mutt_unlink (smimeinfile);
1279 mutt_unlink (certfile);
1285 mutt_wait_filter (thepid);
1286 mutt_unlink (smimeinfile);
1287 mutt_unlink (certfile);
1291 empty = (fgetc (fpout) == EOF);
1296 while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1298 fputs (buf, stdout);
1302 /* pause if there is any error output from SMIME */
1304 mutt_any_key_to_continue (NULL);
1307 /* fatal error while trying to encrypt message */
1309 mutt_any_key_to_continue _("No output from OpenSSL..");
1311 mutt_unlink (tempfile);
1315 t = mutt_new_body ();
1316 t->type = TYPEAPPLICATION;
1317 t->subtype = m_strdup("x-pkcs7-mime");
1318 mutt_set_parameter ("name", "smime.p7m", &t->parameter);
1319 mutt_set_parameter ("smime-type", "enveloped-data", &t->parameter);
1320 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
1322 t->disposition = DISPATTACH;
1323 t->d_filename = m_strdup("smime.p7m");
1324 t->filename = m_strdup(tempfile);
1325 t->unlink = 1; /*delete after sending the message */
1335 BODY *smime_sign_message (BODY * a)
1338 char buffer[LONG_STRING];
1339 char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1340 FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1344 char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1346 if (!intermediates) {
1347 mutt_message (_("Warning: Intermediate certificate not found."));
1348 intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
1351 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
1353 mutt_mktemp (filetosign);
1354 if ((sfp = safe_fopen (filetosign, "w+")) == NULL) {
1355 mutt_perror (filetosign);
1359 mutt_mktemp (signedfile);
1360 if ((smimeout = safe_fopen (signedfile, "w+")) == NULL) {
1361 mutt_perror (signedfile);
1363 mutt_unlink (filetosign);
1367 mutt_write_mime_header (a, sfp);
1369 mutt_write_mime_body (a, sfp);
1374 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1375 NONULL (SmimeKeys), SmimeDefaultKey);
1377 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1378 NONULL (SmimeCertificates), SmimeDefaultKey);
1380 snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1381 NONULL (SmimeCertificates), intermediates);
1385 if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1386 -1, fileno (smimeout), -1,
1387 filetosign)) == -1) {
1388 mutt_perror (_("Can't open OpenSSL subprocess!"));
1391 mutt_unlink (signedfile);
1392 mutt_unlink (filetosign);
1395 fputs (SmimePass, smimein);
1396 fputc ('\n', smimein);
1400 mutt_wait_filter (thepid);
1402 /* check for errors from OpenSSL */
1406 while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1408 fputs (buffer, stdout);
1415 empty = (fgetc (smimeout) == EOF);
1418 mutt_unlink (filetosign);
1422 mutt_any_key_to_continue (NULL);
1425 mutt_any_key_to_continue _("No output from OpenSSL...");
1427 mutt_unlink (signedfile);
1428 return (NULL); /* fatal error while signing */
1431 t = mutt_new_body ();
1432 t->type = TYPEMULTIPART;
1433 t->subtype = m_strdup("signed");
1434 t->encoding = ENC7BIT;
1436 t->disposition = DISPINLINE;
1438 mutt_generate_boundary (&t->parameter);
1439 /* check if this can be extracted from private key somehow.... */
1440 mutt_set_parameter ("micalg", "sha1", &t->parameter);
1441 mutt_set_parameter ("protocol", "application/x-pkcs7-signature",
1447 t->parts->next = mutt_new_body ();
1449 t->type = TYPEAPPLICATION;
1450 t->subtype = m_strdup("x-pkcs7-signature");
1451 t->filename = m_strdup(signedfile);
1452 t->d_filename = m_strdup("smime.p7s");
1454 t->disposition = DISPATTACH;
1455 t->encoding = ENCBASE64;
1456 t->unlink = 1; /* ok to remove this file after sending. */
1468 * Handling S/MIME - bodies.
1477 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1478 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1479 int smimeerrfd, const char *fname,
1480 const char *sig_fname, int opaque)
1482 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1483 smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1484 (opaque ? SmimeVerifyOpaqueCommand :
1485 SmimeVerifyCommand));
1490 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1491 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1492 int smimeerrfd, const char *fname)
1494 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1495 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1496 SmimeCertToUse, NULL, SmimeDecryptCommand);
1501 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1503 char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1504 FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1509 size_t tmplength = 0;
1510 int origType = sigbdy->type;
1511 char *savePrefix = NULL;
1514 snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1516 /* decode to a tempfile, saving the original destination */
1518 if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1519 mutt_perror (signedfile);
1522 /* decoding the attachment changes the size and offset, so save a copy
1523 * of the "real" values now, and restore them after processing
1525 tmplength = sigbdy->length;
1526 tmpoffset = sigbdy->offset;
1528 /* if we are decoding binary bodies, we don't want to prefix each
1529 * line with the prefix or else the data will get corrupted.
1531 savePrefix = s->prefix;
1534 mutt_decode_attachment (sigbdy, s);
1536 sigbdy->length = ftello (s->fpout);
1540 /* restore final destination and substitute the tempfile for input */
1543 s->fpin = fopen (signedfile, "r");
1545 /* restore the prefix */
1546 s->prefix = savePrefix;
1548 sigbdy->type = origType;
1551 mutt_mktemp (smimeerrfile);
1552 if (!(smimeerr = safe_fopen (smimeerrfile, "w+"))) {
1553 mutt_perror (smimeerrfile);
1554 mutt_unlink (signedfile);
1558 crypt_current_time (s, "OpenSSL");
1560 if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1561 -1, -1, fileno (smimeerr),
1562 tempfile, signedfile, 0)) != -1) {
1566 if (mutt_wait_filter (thepid))
1576 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1577 if (linelen && !m_strcasecmp(line, "verification successful"))
1586 mutt_copy_stream (smimeerr, s->fpout);
1589 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1591 mutt_unlink (signedfile);
1592 mutt_unlink (smimeerrfile);
1594 sigbdy->length = tmplength;
1595 sigbdy->offset = tmpoffset;
1597 /* restore the original source stream */
1610 This handles application/pkcs7-mime which can either be a signed
1611 or an encrypted message.
1614 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1619 char buf[HUGE_STRING];
1620 char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1621 char tmpfname[_POSIX_PATH_MAX];
1622 char tmptmpfname[_POSIX_PATH_MAX];
1623 FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1624 FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1628 unsigned int type = mutt_is_application_smime (m);
1630 if (!(type & APPLICATION_SMIME))
1633 mutt_mktemp (outfile);
1634 if ((smimeout = safe_fopen (outfile, "w+")) == NULL) {
1635 mutt_perror (outfile);
1639 mutt_mktemp (errfile);
1640 if ((smimeerr = safe_fopen (errfile, "w+")) == NULL) {
1641 mutt_perror (errfile);
1646 mutt_unlink (errfile);
1649 mutt_mktemp (tmpfname);
1650 if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL) {
1651 mutt_perror (tmpfname);
1659 fseeko (s->fpin, m->offset, 0);
1660 last_pos = m->offset;
1662 mutt_copy_bytes (s->fpin, tmpfp, m->length);
1667 if ((type & ENCRYPT) &&
1668 (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1669 fileno (smimeout), fileno (smimeerr),
1673 mutt_unlink (tmpfname);
1674 if (s->flags & M_DISPLAY)
1675 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1678 else if ((type & SIGNOPAQUE) &&
1679 (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1681 fileno (smimeerr), NULL, tmpfname,
1682 SIGNOPAQUE)) == -1) {
1685 mutt_unlink (tmpfname);
1686 if (s->flags & M_DISPLAY)
1687 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1692 if (type & ENCRYPT) {
1693 if (!smime_valid_passphrase ())
1694 smime_void_passphrase ();
1695 fputs (SmimePass, smimein);
1696 fputc ('\n', smimein);
1701 mutt_wait_filter (thepid);
1702 mutt_unlink (tmpfname);
1705 if (s->flags & M_DISPLAY) {
1708 if ((c = fgetc (smimeerr)) != EOF) {
1709 ungetc (c, smimeerr);
1711 crypt_current_time (s, "OpenSSL");
1712 mutt_copy_stream (smimeerr, s->fpout);
1713 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1717 state_attach_puts (_("[-- The following data is S/MIME"
1718 " encrypted --]\n"), s);
1720 state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1731 mutt_mktemp (tmptmpfname);
1732 if ((fpout = safe_fopen (tmptmpfname, "w+")) == NULL) {
1733 mutt_perror (tmptmpfname);
1739 while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1740 len = m_strlen(buf);
1741 if (len > 1 && buf[len - 2] == '\r') {
1742 buf[len - 2] = '\n';
1743 buf[len - 1] = '\0';
1751 if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1752 fstat (fileno (fpout), &info);
1753 p->length = info.st_size - p->offset;
1755 mutt_parse_part (fpout, p);
1758 tmpfp_buffer = s->fpin;
1760 mutt_body_handler (p, s);
1761 s->fpin = tmpfp_buffer;
1767 mutt_unlink (outfile);
1771 mutt_unlink (tmptmpfname);
1776 if (s->flags & M_DISPLAY) {
1778 state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1780 state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1783 if (type & SIGNOPAQUE) {
1790 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1791 if (linelen && !m_strcasecmp(line, "verification successful"))
1796 m->goodsig = p->goodsig;
1797 m->badsig = p->badsig;
1808 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1812 char tempfile[_POSIX_PATH_MAX];
1814 long tmpoffset = b->offset;
1815 size_t tmplength = b->length;
1816 int origType = b->type;
1820 if (!mutt_is_application_smime (b))
1828 fseeko (s.fpin, b->offset, 0);
1830 mutt_mktemp (tempfile);
1831 if ((tmpfp = safe_fopen (tempfile, "w+")) == NULL) {
1832 mutt_perror (tempfile);
1836 mutt_unlink (tempfile);
1838 mutt_decode_attachment (b, &s);
1840 b->length = ftello (s.fpout);
1846 mutt_mktemp (tempfile);
1847 if ((*fpout = safe_fopen (tempfile, "w+")) == NULL) {
1848 mutt_perror (tempfile);
1852 mutt_unlink (tempfile);
1854 if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1859 (*cur)->goodsig = b->goodsig;
1860 (*cur)->badsig = b->badsig;
1864 b->length = tmplength;
1865 b->offset = tmpoffset;
1867 safe_fclose (&tmpfp);
1874 int smime_application_smime_handler (BODY * m, STATE * s)
1876 return smime_handle_entity (m, s, NULL) ? 0 : -1;
1879 int smime_send_menu (HEADER * msg, int *redraw)
1883 if (!(WithCrypto & APPLICATION_SMIME))
1884 return msg->security;
1886 switch (mutt_multi_choice
1888 ("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1890 case 1: /* (e)ncrypt */
1891 msg->security |= ENCRYPT;
1892 msg->security &= ~SIGN;
1895 case 3: /* encrypt (w)ith */
1898 msg->security |= ENCRYPT;
1901 /* I use "dra" because "123" is recognized anyway */
1902 switch (mutt_multi_choice (_("Choose algorithm family:"
1903 " 1: DES, 2: RC2, 3: AES,"
1904 " or (c)lear? "), _("drac"))) {
1906 switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
1909 str_replace (&SmimeCryptAlg, "des");
1912 str_replace (&SmimeCryptAlg, "des3");
1918 switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
1921 str_replace (&SmimeCryptAlg, "rc2-40");
1924 str_replace (&SmimeCryptAlg, "rc2-64");
1927 str_replace (&SmimeCryptAlg, "rc2-128");
1933 switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
1936 str_replace (&SmimeCryptAlg, "aes128");
1939 str_replace (&SmimeCryptAlg, "aes192");
1942 str_replace (&SmimeCryptAlg, "aes256");
1947 case 4: /* (c)lear */
1948 p_delete(&SmimeCryptAlg);
1950 case -1: /* Ctrl-G or Enter */
1954 } while (choice == -1);
1958 case 2: /* (s)ign */
1960 if (!SmimeDefaultKey)
1961 mutt_message (_("Can't sign: No key specified. Use Sign As."));
1964 msg->security |= SIGN;
1965 msg->security &= ~ENCRYPT;
1969 case 4: /* sign (a)s */
1971 if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1972 str_replace (&SmimeDefaultKey, p);
1974 msg->security |= SIGN;
1976 /* probably need a different passphrase */
1977 crypt_smime_void_passphrase ();
1981 msg->security &= ~SIGN;
1984 *redraw = REDRAW_FULL;
1987 case 5: /* (b)oth */
1988 msg->security |= (ENCRYPT | SIGN);
1991 case 6: /* (f)orget it */
1992 case 7: /* (c)lear */
1997 if (msg->security && msg->security != APPLICATION_SMIME)
1998 msg->security |= APPLICATION_SMIME;
2002 return (msg->security);
2006 #endif /* CRYPT_BACKEND_CLASSIC_SMIME */