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>
20 #include <lib-lib/debug.h>
22 #include <lib-mime/mime.h>
24 #include <lib-ui/curses.h>
25 #include <lib-ui/enter.h>
26 #include <lib-ui/menu.h>
45 #ifdef HAVE_SYS_TIME_H
46 # include <sys/time.h>
49 #ifdef HAVE_SYS_RESOURCE_H
50 # include <sys/resource.h>
55 struct smime_command_context {
56 const char *key; /* %k */
57 const char *cryptalg; /* %a */
58 const char *fname; /* %f */
59 const char *sig_fname; /* %s */
60 const char *certificates; /* %c */
61 const char *intermediates; /* %i */
70 char trust; /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
71 short public; /* 1=public 0=private */
75 char SmimePass[STRING];
76 time_t SmimeExptime = 0; /* when does the cached passphrase expire? */
79 static char SmimeKeyToUse[_POSIX_PATH_MAX] = { 0 };
80 static char SmimeCertToUse[_POSIX_PATH_MAX];
81 static char SmimeIntermediateToUse[_POSIX_PATH_MAX];
85 * Create a format string to be used with scanf.
86 * To use it, write, for instance, MUTT_FORMAT(HUGE_STRING).
88 * See K&R 2nd ed, p. 231 for an explanation.
90 #define _MUTT_FORMAT_2(a,b) "%" a b
91 #define _MUTT_FORMAT_1(a, b) _MUTT_FORMAT_2(#a, b)
92 #define MUTT_FORMAT(a) _MUTT_FORMAT_1(a, "s")
96 * Queries and passphrase handling.
100 /* these are copies from pgp.c */
103 void smime_void_passphrase (void)
105 p_clear(SmimePass, countof(SmimePass));
109 int smime_valid_passphrase (void)
111 time_t now = time (NULL);
113 if (now < SmimeExptime)
114 /* Use cached copy. */
117 smime_void_passphrase ();
119 if (mutt_get_field_unbuffered (_("Enter S/MIME passphrase:"), SmimePass,
120 sizeof (SmimePass), M_PASS) == 0) {
121 SmimeExptime = time (NULL) + SmimeTimeout;
132 * The OpenSSL interface
135 /* This is almost identical to ppgp's invoking interface. */
137 static const char *_mutt_fmt_smime_command (char *dest,
142 const char *ifstring,
143 const char *elsestring,
148 struct smime_command_context *cctx = (struct smime_command_context *) data;
149 int optional = (flags & M_FORMAT_OPTIONAL);
155 char path[_POSIX_PATH_MAX];
156 char buf1[LONG_STRING], buf2[LONG_STRING];
159 m_strcpy(path, sizeof(path), NONULL(SmimeCALocation));
160 mutt_expand_path (path, sizeof (path));
161 mutt_quote_filename (buf1, sizeof (buf1), path);
163 if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
164 snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
166 snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
168 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
169 snprintf (dest, destlen, fmt, buf2);
171 else if (!SmimeCALocation)
177 { /* certificate (list) */
179 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
180 snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
182 else if (!cctx->certificates)
188 { /* intermediate certificates */
190 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
191 snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
193 else if (!cctx->intermediates)
199 { /* detached signature */
201 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
202 snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
204 else if (!cctx->sig_fname)
212 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
213 snprintf (dest, destlen, fmt, NONULL (cctx->key));
221 { /* algorithm for encryption */
223 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
224 snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
232 { /* file to process */
234 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
235 snprintf (dest, destlen, fmt, NONULL (cctx->fname));
237 else if (!cctx->fname)
248 mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
250 else if (flags & M_FORMAT_OPTIONAL)
251 mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
259 static void mutt_smime_command (char *d, size_t dlen,
260 struct smime_command_context *cctx,
263 mutt_FormatString (d, dlen, NONULL (fmt), _mutt_fmt_smime_command,
264 (unsigned long) cctx, 0);
265 debug_print (2, ("%s\n", d));
268 static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
269 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
270 int smimeerrfd, const char *fname,
271 const char *sig_fname, const char *cryptalg,
272 const char *key, const char *certificates,
273 const char *intermediates, const char *format)
275 struct smime_command_context cctx;
276 char cmd[HUGE_STRING];
280 if (!format || !*format)
284 cctx.sig_fname = sig_fname;
286 cctx.cryptalg = cryptalg;
287 cctx.certificates = certificates;
288 cctx.intermediates = intermediates;
290 mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
292 return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
293 smimeinfd, smimeoutfd, smimeerrfd);
302 * Key and certificate handling.
308 Search the certificate index for given mailbox.
309 return certificate file name.
312 static void smime_entry (char *s, size_t l, MUTTMENU * menu, int num)
314 smime_id *Table = (smime_id *) menu->data;
315 smime_id this = Table[num];
316 const char *truststate;
318 switch (this.trust) {
320 truststate = N_("Trusted ");
323 truststate = N_("Verified ");
326 truststate = N_("Unverified");
329 truststate = N_("Expired ");
332 truststate = N_("Revoked ");
335 truststate = N_("Invalid ");
338 truststate = N_("Unknown ");
341 snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
342 truststate, this.email, this.nick);
344 snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
345 this.email, this.nick);
352 char *smime_ask_for_key (char *prompt, char *mailbox, short public)
356 long cert_num; /* Will contain the number of certificates.
357 * To be able to get it, the .index file will be read twice... */
358 char index_file[_POSIX_PATH_MAX];
360 char buf[LONG_STRING];
361 char fields[5][STRING];
362 int numFields, hash_suffix, done, cur; /* The current entry */
365 char helpstr[HUGE_STRING * 3];
370 prompt = _("Enter keyID: ");
371 snprintf (index_file, sizeof (index_file), "%s/.index",
372 public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));
374 index = fopen (index_file, "r");
376 mutt_perror (index_file);
381 while (!feof (index)) {
382 if (fgets (buf, sizeof (buf), index))
389 if (mutt_get_field (prompt, qry, sizeof (qry), 0))
391 snprintf (title, sizeof (title),
392 _("S/MIME certificates matching \"%s\"."), qry);
395 index = fopen (index_file, "r");
397 mutt_perror (index_file);
402 Table = p_new(smime_id, cert_num);
403 while (!feof (index)) {
405 fscanf (index, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
406 fields[0], &hash, &hash_suffix, fields[2]);
408 fscanf (index, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
409 fields[3], fields[4]);
411 /* 0=email 1=name 2=nick 3=intermediate 4=trust */
415 /* Check if query matches this certificate */
416 if (!m_stristr(fields[0], qry) && !m_stristr(fields[2], qry))
419 Table[cur].hash = hash;
420 Table[cur].suffix = hash_suffix;
421 m_strcpy(Table[cur].email, sizeof(Table[cur].email), fields[0]);
422 m_strcpy(Table[cur].nick, sizeof(Table[cur].nick), fields[2]);
423 Table[cur].trust = *fields[4];
424 Table[cur].public = public;
430 /* Make Helpstring */
432 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT);
433 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
434 mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME,
435 OP_GENERIC_SELECT_ENTRY);
436 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
437 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
438 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
440 /* Create the menu */
441 menu = mutt_new_menu ();
443 menu->make_entry = smime_entry;
444 menu->menu = MENU_SMIME;
445 menu->help = helpstr;
448 /* sorting keys might be done later - TODO */
455 switch (mutt_menuLoop (menu)) {
456 case OP_GENERIC_SELECT_ENTRY:
468 fname = p_new(char, 13); /* Hash + '.' + Suffix + \0 */
469 sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
474 mutt_menuDestroy (&menu);
476 set_option (OPTNEEDREDRAW);
485 char *smime_get_field_from_db (char *mailbox, char *query, short public,
488 int addr_len, query_len, found = 0, ask = 0, choice = 0;
489 char cert_path[_POSIX_PATH_MAX];
490 char buf[LONG_STRING], prompt[STRING];
491 char fields[5][STRING];
495 char key_trust_level = 0;
498 if (!mailbox && !query)
501 addr_len = mailbox ? m_strlen(mailbox) : 0;
502 query_len = query ? m_strlen(query) : 0;
506 /* index-file format:
507 mailbox certfile label issuer_certfile trust_flags\n
509 certfile is a hash value generated by openssl.
510 Note that this was done according to the OpenSSL
511 specs on their CA-directory.
514 snprintf (cert_path, sizeof (cert_path), "%s/.index",
515 (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));
517 if (!stat (cert_path, &info)) {
518 if ((fp = safe_fopen (cert_path, "r")) == NULL) {
519 mutt_perror (cert_path);
523 while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
524 if (mailbox && !(m_strncasecmp(mailbox, buf, addr_len))) {
525 numFields = sscanf (buf,
526 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
527 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
528 MUTT_FORMAT (STRING) "\n",
529 fields[0], fields[1],
530 fields[2], fields[3], fields[4]);
533 if (mailbox && public &&
535 *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
539 if (public && *fields[4] == 'u')
540 snprintf (prompt, sizeof (prompt),
542 ("ID %s is unverified. Do you want to use it for %s ?"),
544 else if (public && *fields[4] == 'v')
545 snprintf (prompt, sizeof (prompt),
546 _("Use (untrusted!) ID %s for %s ?"),
549 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
553 if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
559 else if (choice == M_NO) {
563 else if (choice == M_YES) {
564 m_strcpy(key, sizeof(key), fields[1]);
571 key_trust_level = *fields[4];
572 m_strcpy(key, sizeof(key), fields[1]);
577 numFields = sscanf (buf,
578 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
579 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
580 MUTT_FORMAT (STRING) "\n",
581 fields[0], fields[1],
582 fields[2], fields[3], fields[4]);
584 /* query = label: return certificate. */
585 if (numFields >= 3 &&
586 !(m_strncasecmp(query, fields[2], query_len))) {
588 m_strcpy(key, sizeof(key), fields[1]);
590 /* query = certificate: return intermediate certificate. */
591 else if (numFields >= 4 &&
592 !(m_strncasecmp(query, fields[1], query_len))) {
594 m_strcpy(key, sizeof(key), fields[3]);
601 if (public && *fields[4] == 'u')
602 snprintf (prompt, sizeof (prompt),
603 _("ID %s is unverified. Do you want to use it for %s ?"),
605 else if (public && *fields[4] == 'v')
606 snprintf (prompt, sizeof (prompt),
607 _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
609 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
611 choice = mutt_yesorno (prompt, M_NO);
612 if (choice == -1 || choice == M_NO)
615 else if (key_trust_level && may_ask) {
616 if (key_trust_level == 'u') {
617 snprintf (prompt, sizeof (prompt),
618 _("ID %s is unverified. Do you want to use it for %s ?"),
620 choice = mutt_yesorno (prompt, M_NO);
624 else if (key_trust_level == 'v') {
626 ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
634 /* Note: m_strdup("") returns NULL. */
635 return m_strdup(key);
642 This sets the '*ToUse' variables for an upcoming decryption, where
643 the reuquired key is different from SmimeDefaultKey.
646 void _smime_getkeys (char *mailbox)
651 k = smime_get_field_from_db (mailbox, NULL, 0, 1);
654 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
655 k = smime_ask_for_key (buf, mailbox, 0);
659 /* the key used last time. */
660 if (*SmimeKeyToUse &&
661 !m_strcasecmp(k, SmimeKeyToUse + m_strlen(SmimeKeys) + 1)) {
666 smime_void_passphrase ();
668 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
669 NONULL (SmimeKeys), k);
671 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
672 NONULL (SmimeCertificates), k);
674 if (m_strcasecmp(k, SmimeDefaultKey))
675 smime_void_passphrase ();
681 if (*SmimeKeyToUse) {
682 if (!m_strcasecmp(SmimeDefaultKey,
683 SmimeKeyToUse + m_strlen(SmimeKeys) + 1))
686 smime_void_passphrase ();
689 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
690 NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
692 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
693 NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
696 void smime_getkeys (ENVELOPE * env)
701 if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
702 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
703 NONULL (SmimeKeys), SmimeDefaultKey);
705 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
706 NONULL (SmimeCertificates), SmimeDefaultKey);
711 for (t = env->to; !found && t; t = t->next)
712 if (mutt_addr_is_user (t)) {
714 _smime_getkeys (t->mailbox);
716 for (t = env->cc; !found && t; t = t->next)
717 if (mutt_addr_is_user (t)) {
719 _smime_getkeys (t->mailbox);
721 if (!found && (t = mutt_default_from ())) {
722 _smime_getkeys (t->mailbox);
727 /* This routine attempts to find the keyids of the recipients of a message.
728 * It returns NULL if any of the keys can not be found.
731 char *smime_findKeys (address_t * to, address_t * cc, address_t * bcc)
733 char *keyID, *keylist = NULL;
734 size_t keylist_size = 0;
735 size_t keylist_used = 0;
736 address_t *tmp = NULL, *addr = NULL;
737 address_t **last = &tmp;
741 const char *fqdn = mutt_fqdn (1);
743 for (i = 0; i < 3; i++) {
758 *last = address_list_dup (p);
760 last = &((*last)->next);
764 rfc822_qualify (tmp, fqdn);
766 tmp = mutt_remove_duplicates (tmp);
768 for (p = tmp; p; p = p->next) {
769 char buf[LONG_STRING];
773 if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
774 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
775 keyID = smime_ask_for_key (buf, q->mailbox, 1);
778 mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
780 address_delete (&tmp);
781 address_delete (&addr);
785 keylist_size += m_strlen(keyID) + 2;
786 p_realloc(&keylist, keylist_size);
787 sprintf (keylist + keylist_used, "%s\n", keyID); /* __SPRINTF_CHECKED__ */
788 keylist_used = m_strlen(keylist);
790 address_delete (&addr);
793 address_delete (&tmp);
802 static int smime_handle_cert_email (char *certificate, char *mailbox,
803 int copy, char ***buffer, int *num)
805 FILE *fpout = NULL, *fperr = NULL;
806 char tmpfname[_POSIX_PATH_MAX];
808 int ret = -1, count = 0;
811 mutt_mktemp (tmpfname);
812 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
813 mutt_perror (tmpfname);
816 mutt_unlink (tmpfname);
818 mutt_mktemp (tmpfname);
819 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
821 mutt_perror (tmpfname);
824 mutt_unlink (tmpfname);
826 if ((thepid = smime_invoke (NULL, NULL, NULL,
827 -1, fileno (fpout), fileno (fperr),
828 certificate, NULL, NULL, NULL, NULL, NULL,
829 SmimeGetCertEmailCommand)) == -1) {
830 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
836 mutt_wait_filter (thepid);
844 while ((fgets (email, sizeof (email), fpout))) {
845 *(email + m_strlen(email) - 1) = '\0';
846 if (m_strncasecmp(email, mailbox, m_strlen(mailbox)) == 0)
849 ret = ret < 0 ? 0 : ret;
855 mutt_copy_stream (fperr, stdout);
856 mutt_any_key_to_continue (_
857 ("Error: unable to create OpenSSL subprocess!"));
865 if (copy && buffer && num) {
867 *buffer = p_new(char *, count);
871 while ((fgets (email, sizeof (email), fpout))) {
872 *(email + m_strlen(email) - 1) = '\0';
873 (*buffer)[count] = p_dupstr(email, m_strlen(email));
888 static char *smime_extract_certificate (char *infile)
890 FILE *fpout = NULL, *fperr = NULL;
891 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
892 char tmpfname[_POSIX_PATH_MAX];
897 mutt_mktemp (tmpfname);
898 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
899 mutt_perror (tmpfname);
902 mutt_unlink (tmpfname);
904 mutt_mktemp (pk7out);
905 if ((fpout = safe_fopen (pk7out, "w+")) == NULL) {
907 mutt_perror (pk7out);
911 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
912 extract the full set of certificates directly.
914 if ((thepid = smime_invoke (NULL, NULL, NULL,
915 -1, fileno (fpout), fileno (fperr),
916 infile, NULL, NULL, NULL, NULL, NULL,
917 SmimePk7outCommand)) == -1) {
918 mutt_any_key_to_continue (_
919 ("Error: unable to create OpenSSL subprocess!"));
922 mutt_unlink (pk7out);
926 mutt_wait_filter (thepid);
933 empty = (fgetc (fpout) == EOF);
935 mutt_perror (pk7out);
936 mutt_copy_stream (fperr, stdout);
939 mutt_unlink (pk7out);
946 mutt_mktemp (certfile);
947 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
949 mutt_unlink (pk7out);
950 mutt_perror (certfile);
954 /* Step 2: Extract the certificates from a PKCS#7 structure.
956 if ((thepid = smime_invoke (NULL, NULL, NULL,
957 -1, fileno (fpout), fileno (fperr),
958 pk7out, NULL, NULL, NULL, NULL, NULL,
959 SmimeGetCertCommand)) == -1) {
960 mutt_any_key_to_continue (_
961 ("Error: unable to create OpenSSL subprocess!"));
964 mutt_unlink (pk7out);
965 mutt_unlink (certfile);
969 mutt_wait_filter (thepid);
971 mutt_unlink (pk7out);
977 empty = (fgetc (fpout) == EOF);
979 mutt_copy_stream (fperr, stdout);
982 mutt_unlink (certfile);
989 return m_strdup(certfile);
992 static char *smime_extract_signer_certificate (char *infile)
994 FILE *fpout = NULL, *fperr = NULL;
995 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
996 char tmpfname[_POSIX_PATH_MAX];
1001 mutt_mktemp (tmpfname);
1002 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1003 mutt_perror (tmpfname);
1006 mutt_unlink (tmpfname);
1009 mutt_mktemp (certfile);
1010 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
1012 mutt_perror (certfile);
1016 /* Extract signer's certificate
1018 if ((thepid = smime_invoke (NULL, NULL, NULL,
1019 -1, -1, fileno (fperr),
1020 infile, NULL, NULL, NULL, certfile, NULL,
1021 SmimeGetSignerCertCommand)) == -1) {
1022 mutt_any_key_to_continue (_
1023 ("Error: unable to create OpenSSL subprocess!"));
1026 mutt_unlink (pk7out);
1027 mutt_unlink (certfile);
1031 mutt_wait_filter (thepid);
1037 empty = (fgetc (fpout) == EOF);
1040 mutt_copy_stream (fperr, stdout);
1041 mutt_any_key_to_continue (NULL);
1044 mutt_unlink (certfile);
1051 return m_strdup(certfile);
1057 /* Add a certificate and update index file (externally). */
1059 void smime_invoke_import (char *infile, char *mailbox)
1061 char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1062 FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1065 mutt_mktemp (tmpfname);
1066 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1067 mutt_perror (tmpfname);
1070 mutt_unlink (tmpfname);
1072 mutt_mktemp (tmpfname);
1073 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
1075 mutt_perror (tmpfname);
1078 mutt_unlink (tmpfname);
1082 if (option (OPTASKCERTLABEL))
1083 mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1086 if ((certfile = smime_extract_certificate (infile))) {
1089 if ((thepid = smime_invoke (&smimein, NULL, NULL,
1090 -1, fileno (fpout), fileno (fperr),
1091 certfile, NULL, NULL, NULL, NULL, NULL,
1092 SmimeImportCertCommand)) == -1) {
1093 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1096 fputs (buf, smimein);
1097 fputc ('\n', smimein);
1100 mutt_wait_filter (thepid);
1102 mutt_unlink (certfile);
1103 p_delete(&certfile);
1111 mutt_copy_stream (fpout, stdout);
1112 mutt_copy_stream (fperr, stdout);
1121 int smime_verify_sender (HEADER * h)
1123 char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1127 mutt_mktemp (tempfname);
1128 if (!(fpout = safe_fopen (tempfname, "w"))) {
1129 mutt_perror (tempfname);
1133 if (h->security & ENCRYPT)
1134 mutt_copy_message (fpout, Context, h,
1135 M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1136 CH_MIME | CH_WEED | CH_NONEWLINE);
1138 mutt_copy_message (fpout, Context, h, 0, 0);
1144 h->env->from = mutt_expand_aliases (h->env->from);
1145 mbox = h->env->from->mailbox;
1147 else if (h->env->sender) {
1148 h->env->sender = mutt_expand_aliases (h->env->sender);
1149 mbox = h->env->sender->mailbox;
1153 if ((certfile = smime_extract_signer_certificate (tempfname))) {
1154 mutt_unlink (tempfname);
1155 if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1157 mutt_any_key_to_continue (NULL);
1161 mutt_unlink (certfile);
1162 p_delete(&certfile);
1165 mutt_any_key_to_continue (_("no certfile"));
1168 mutt_any_key_to_continue (_("no mbox"));
1170 mutt_unlink (tempfname);
1183 * Creating S/MIME - bodies.
1190 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1191 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1192 int smimeerrfd, const char *fname,
1195 return smime_invoke (smimein, smimeout, smimeerr,
1196 smimeinfd, smimeoutfd, smimeerrfd,
1197 fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1198 SmimeEncryptCommand);
1203 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1204 int smimeinfd, int smimeoutfd, int smimeerrfd,
1207 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1208 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1209 SmimeCertToUse, SmimeIntermediateToUse,
1216 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1218 char buf[LONG_STRING], certfile[LONG_STRING];
1219 char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1220 char smimeinfile[_POSIX_PATH_MAX];
1221 char *cert_start = certlist, *cert_end = certlist;
1222 FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1227 mutt_mktemp (tempfile);
1228 if ((fpout = safe_fopen (tempfile, "w+")) == NULL) {
1229 mutt_perror (tempfile);
1233 mutt_mktemp (smimeerrfile);
1234 if ((smimeerr = safe_fopen (smimeerrfile, "w+")) == NULL) {
1235 mutt_perror (smimeerrfile);
1237 mutt_unlink (tempfile);
1240 mutt_unlink (smimeerrfile);
1242 mutt_mktemp (smimeinfile);
1243 if ((fptmp = safe_fopen (smimeinfile, "w+")) == NULL) {
1244 mutt_perror (smimeinfile);
1245 mutt_unlink (tempfile);
1253 int off = m_strlen(certfile);
1255 while (*++cert_end && *cert_end != '\n');
1259 snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1260 NONULL (SmimeCertificates), cert_start);
1262 cert_start = cert_end;
1266 /* write a MIME entity */
1267 mutt_write_mime_header (a, fptmp);
1268 fputc ('\n', fptmp);
1269 mutt_write_mime_body (a, fptmp);
1273 smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1274 fileno (fpout), fileno (smimeerr),
1275 smimeinfile, certfile)) == -1) {
1277 mutt_unlink (smimeinfile);
1278 mutt_unlink (certfile);
1284 mutt_wait_filter (thepid);
1285 mutt_unlink (smimeinfile);
1286 mutt_unlink (certfile);
1290 empty = (fgetc (fpout) == EOF);
1295 while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1297 fputs (buf, stdout);
1301 /* pause if there is any error output from SMIME */
1303 mutt_any_key_to_continue (NULL);
1306 /* fatal error while trying to encrypt message */
1308 mutt_any_key_to_continue _("No output from OpenSSL..");
1310 mutt_unlink (tempfile);
1314 t = mutt_new_body ();
1315 t->type = TYPEAPPLICATION;
1316 t->subtype = m_strdup("x-pkcs7-mime");
1317 mutt_set_parameter ("name", "smime.p7m", &t->parameter);
1318 mutt_set_parameter ("smime-type", "enveloped-data", &t->parameter);
1319 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
1321 t->disposition = DISPATTACH;
1322 t->d_filename = m_strdup("smime.p7m");
1323 t->filename = m_strdup(tempfile);
1324 t->unlink = 1; /*delete after sending the message */
1334 BODY *smime_sign_message (BODY * a)
1337 char buffer[LONG_STRING];
1338 char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1339 FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1343 char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1345 if (!intermediates) {
1346 mutt_message (_("Warning: Intermediate certificate not found."));
1347 intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
1350 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
1352 mutt_mktemp (filetosign);
1353 if ((sfp = safe_fopen (filetosign, "w+")) == NULL) {
1354 mutt_perror (filetosign);
1358 mutt_mktemp (signedfile);
1359 if ((smimeout = safe_fopen (signedfile, "w+")) == NULL) {
1360 mutt_perror (signedfile);
1362 mutt_unlink (filetosign);
1366 mutt_write_mime_header (a, sfp);
1368 mutt_write_mime_body (a, sfp);
1373 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1374 NONULL (SmimeKeys), SmimeDefaultKey);
1376 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1377 NONULL (SmimeCertificates), SmimeDefaultKey);
1379 snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1380 NONULL (SmimeCertificates), intermediates);
1384 if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1385 -1, fileno (smimeout), -1,
1386 filetosign)) == -1) {
1387 mutt_perror (_("Can't open OpenSSL subprocess!"));
1390 mutt_unlink (signedfile);
1391 mutt_unlink (filetosign);
1394 fputs (SmimePass, smimein);
1395 fputc ('\n', smimein);
1399 mutt_wait_filter (thepid);
1401 /* check for errors from OpenSSL */
1405 while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1407 fputs (buffer, stdout);
1414 empty = (fgetc (smimeout) == EOF);
1417 mutt_unlink (filetosign);
1421 mutt_any_key_to_continue (NULL);
1424 mutt_any_key_to_continue _("No output from OpenSSL...");
1426 mutt_unlink (signedfile);
1427 return (NULL); /* fatal error while signing */
1430 t = mutt_new_body ();
1431 t->type = TYPEMULTIPART;
1432 t->subtype = m_strdup("signed");
1433 t->encoding = ENC7BIT;
1435 t->disposition = DISPINLINE;
1437 mutt_generate_boundary (&t->parameter);
1438 /* check if this can be extracted from private key somehow.... */
1439 mutt_set_parameter ("micalg", "sha1", &t->parameter);
1440 mutt_set_parameter ("protocol", "application/x-pkcs7-signature",
1446 t->parts->next = mutt_new_body ();
1448 t->type = TYPEAPPLICATION;
1449 t->subtype = m_strdup("x-pkcs7-signature");
1450 t->filename = m_strdup(signedfile);
1451 t->d_filename = m_strdup("smime.p7s");
1453 t->disposition = DISPATTACH;
1454 t->encoding = ENCBASE64;
1455 t->unlink = 1; /* ok to remove this file after sending. */
1467 * Handling S/MIME - bodies.
1476 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1477 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1478 int smimeerrfd, const char *fname,
1479 const char *sig_fname, int opaque)
1481 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1482 smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1483 (opaque ? SmimeVerifyOpaqueCommand :
1484 SmimeVerifyCommand));
1489 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1490 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1491 int smimeerrfd, const char *fname)
1493 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1494 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1495 SmimeCertToUse, NULL, SmimeDecryptCommand);
1500 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1502 char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1503 FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1508 size_t tmplength = 0;
1509 int origType = sigbdy->type;
1510 char *savePrefix = NULL;
1513 snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1515 /* decode to a tempfile, saving the original destination */
1517 if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1518 mutt_perror (signedfile);
1521 /* decoding the attachment changes the size and offset, so save a copy
1522 * of the "real" values now, and restore them after processing
1524 tmplength = sigbdy->length;
1525 tmpoffset = sigbdy->offset;
1527 /* if we are decoding binary bodies, we don't want to prefix each
1528 * line with the prefix or else the data will get corrupted.
1530 savePrefix = s->prefix;
1533 mutt_decode_attachment (sigbdy, s);
1535 sigbdy->length = ftello (s->fpout);
1539 /* restore final destination and substitute the tempfile for input */
1542 s->fpin = fopen (signedfile, "r");
1544 /* restore the prefix */
1545 s->prefix = savePrefix;
1547 sigbdy->type = origType;
1550 mutt_mktemp (smimeerrfile);
1551 if (!(smimeerr = safe_fopen (smimeerrfile, "w+"))) {
1552 mutt_perror (smimeerrfile);
1553 mutt_unlink (signedfile);
1557 crypt_current_time (s, "OpenSSL");
1559 if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1560 -1, -1, fileno (smimeerr),
1561 tempfile, signedfile, 0)) != -1) {
1565 if (mutt_wait_filter (thepid))
1575 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1576 if (linelen && !m_strcasecmp(line, "verification successful"))
1585 mutt_copy_stream (smimeerr, s->fpout);
1588 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1590 mutt_unlink (signedfile);
1591 mutt_unlink (smimeerrfile);
1593 sigbdy->length = tmplength;
1594 sigbdy->offset = tmpoffset;
1596 /* restore the original source stream */
1609 This handles application/pkcs7-mime which can either be a signed
1610 or an encrypted message.
1613 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1618 char buf[HUGE_STRING];
1619 char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1620 char tmpfname[_POSIX_PATH_MAX];
1621 char tmptmpfname[_POSIX_PATH_MAX];
1622 FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1623 FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1627 unsigned int type = mutt_is_application_smime (m);
1629 if (!(type & APPLICATION_SMIME))
1632 mutt_mktemp (outfile);
1633 if ((smimeout = safe_fopen (outfile, "w+")) == NULL) {
1634 mutt_perror (outfile);
1638 mutt_mktemp (errfile);
1639 if ((smimeerr = safe_fopen (errfile, "w+")) == NULL) {
1640 mutt_perror (errfile);
1645 mutt_unlink (errfile);
1648 mutt_mktemp (tmpfname);
1649 if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL) {
1650 mutt_perror (tmpfname);
1658 fseeko (s->fpin, m->offset, 0);
1659 last_pos = m->offset;
1661 mutt_copy_bytes (s->fpin, tmpfp, m->length);
1666 if ((type & ENCRYPT) &&
1667 (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1668 fileno (smimeout), fileno (smimeerr),
1672 mutt_unlink (tmpfname);
1673 if (s->flags & M_DISPLAY)
1674 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1677 else if ((type & SIGNOPAQUE) &&
1678 (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1680 fileno (smimeerr), NULL, tmpfname,
1681 SIGNOPAQUE)) == -1) {
1684 mutt_unlink (tmpfname);
1685 if (s->flags & M_DISPLAY)
1686 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1691 if (type & ENCRYPT) {
1692 if (!smime_valid_passphrase ())
1693 smime_void_passphrase ();
1694 fputs (SmimePass, smimein);
1695 fputc ('\n', smimein);
1700 mutt_wait_filter (thepid);
1701 mutt_unlink (tmpfname);
1704 if (s->flags & M_DISPLAY) {
1707 if ((c = fgetc (smimeerr)) != EOF) {
1708 ungetc (c, smimeerr);
1710 crypt_current_time (s, "OpenSSL");
1711 mutt_copy_stream (smimeerr, s->fpout);
1712 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1716 state_attach_puts (_("[-- The following data is S/MIME"
1717 " encrypted --]\n"), s);
1719 state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1730 mutt_mktemp (tmptmpfname);
1731 if ((fpout = safe_fopen (tmptmpfname, "w+")) == NULL) {
1732 mutt_perror (tmptmpfname);
1738 while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1739 len = m_strlen(buf);
1740 if (len > 1 && buf[len - 2] == '\r') {
1741 buf[len - 2] = '\n';
1742 buf[len - 1] = '\0';
1750 if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1751 fstat (fileno (fpout), &info);
1752 p->length = info.st_size - p->offset;
1754 mutt_parse_part (fpout, p);
1757 tmpfp_buffer = s->fpin;
1759 mutt_body_handler (p, s);
1760 s->fpin = tmpfp_buffer;
1766 mutt_unlink (outfile);
1770 mutt_unlink (tmptmpfname);
1775 if (s->flags & M_DISPLAY) {
1777 state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1779 state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1782 if (type & SIGNOPAQUE) {
1789 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1790 if (linelen && !m_strcasecmp(line, "verification successful"))
1795 m->goodsig = p->goodsig;
1796 m->badsig = p->badsig;
1807 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1811 char tempfile[_POSIX_PATH_MAX];
1813 long tmpoffset = b->offset;
1814 size_t tmplength = b->length;
1815 int origType = b->type;
1819 if (!mutt_is_application_smime (b))
1827 fseeko (s.fpin, b->offset, 0);
1829 mutt_mktemp (tempfile);
1830 if ((tmpfp = safe_fopen (tempfile, "w+")) == NULL) {
1831 mutt_perror (tempfile);
1835 mutt_unlink (tempfile);
1837 mutt_decode_attachment (b, &s);
1839 b->length = ftello (s.fpout);
1845 mutt_mktemp (tempfile);
1846 if ((*fpout = safe_fopen (tempfile, "w+")) == NULL) {
1847 mutt_perror (tempfile);
1851 mutt_unlink (tempfile);
1853 if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1858 (*cur)->goodsig = b->goodsig;
1859 (*cur)->badsig = b->badsig;
1863 b->length = tmplength;
1864 b->offset = tmpoffset;
1866 safe_fclose (&tmpfp);
1873 int smime_application_smime_handler (BODY * m, STATE * s)
1875 return smime_handle_entity (m, s, NULL) ? 0 : -1;
1878 int smime_send_menu (HEADER * msg, int *redraw)
1882 switch (mutt_multi_choice
1883 (_("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1885 case 1: /* (e)ncrypt */
1886 msg->security |= ENCRYPT;
1887 msg->security &= ~SIGN;
1890 case 3: /* encrypt (w)ith */
1893 msg->security |= ENCRYPT;
1896 /* I use "dra" because "123" is recognized anyway */
1897 switch (mutt_multi_choice (_("Choose algorithm family:"
1898 " 1: DES, 2: RC2, 3: AES,"
1899 " or (c)lear? "), _("drac"))) {
1901 switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
1904 m_strreplace(&SmimeCryptAlg, "des");
1907 m_strreplace(&SmimeCryptAlg, "des3");
1913 switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
1916 m_strreplace(&SmimeCryptAlg, "rc2-40");
1919 m_strreplace(&SmimeCryptAlg, "rc2-64");
1922 m_strreplace(&SmimeCryptAlg, "rc2-128");
1928 switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
1931 m_strreplace(&SmimeCryptAlg, "aes128");
1934 m_strreplace(&SmimeCryptAlg, "aes192");
1937 m_strreplace(&SmimeCryptAlg, "aes256");
1942 case 4: /* (c)lear */
1943 p_delete(&SmimeCryptAlg);
1945 case -1: /* Ctrl-G or Enter */
1949 } while (choice == -1);
1953 case 2: /* (s)ign */
1955 if (!SmimeDefaultKey)
1956 mutt_message (_("Can't sign: No key specified. Use Sign As."));
1959 msg->security |= SIGN;
1960 msg->security &= ~ENCRYPT;
1964 case 4: /* sign (a)s */
1966 if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1967 m_strreplace(&SmimeDefaultKey, p);
1969 msg->security |= SIGN;
1971 /* probably need a different passphrase */
1972 crypt_smime_void_passphrase ();
1976 msg->security &= ~SIGN;
1979 *redraw = REDRAW_FULL;
1982 case 5: /* (b)oth */
1983 msg->security |= (ENCRYPT | SIGN);
1986 case 6: /* (f)orget it */
1987 case 7: /* (c)lear */
1992 if (msg->security && msg->security != APPLICATION_SMIME)
1993 msg->security |= APPLICATION_SMIME;
1997 return (msg->security);