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. */
138 _mutt_fmt_smime_command (char *dest, ssize_t destlen, char op,
139 const char *src, const char *prefix,
140 const char *ifstring, const char *elsestring,
141 unsigned long data, format_flag flags)
144 struct smime_command_context *cctx = (struct smime_command_context *) data;
145 int optional = (flags & M_FORMAT_OPTIONAL);
151 char path[_POSIX_PATH_MAX];
152 char buf1[LONG_STRING], buf2[LONG_STRING];
155 m_strcpy(path, sizeof(path), NONULL(SmimeCALocation));
156 mutt_expand_path (path, sizeof (path));
157 mutt_quote_filename (buf1, sizeof (buf1), path);
159 if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
160 snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
162 snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
164 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
165 snprintf (dest, destlen, fmt, buf2);
167 else if (!SmimeCALocation)
173 { /* certificate (list) */
175 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
176 snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
178 else if (!cctx->certificates)
184 { /* intermediate certificates */
186 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
187 snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
189 else if (!cctx->intermediates)
195 { /* detached signature */
197 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
198 snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
200 else if (!cctx->sig_fname)
208 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
209 snprintf (dest, destlen, fmt, NONULL (cctx->key));
217 { /* algorithm for encryption */
219 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
220 snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
228 { /* file to process */
230 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
231 snprintf (dest, destlen, fmt, NONULL (cctx->fname));
233 else if (!cctx->fname)
244 mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
246 else if (flags & M_FORMAT_OPTIONAL)
247 mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
255 static void mutt_smime_command (char *d, ssize_t dlen,
256 struct smime_command_context *cctx,
259 mutt_FormatString (d, dlen, NONULL (fmt), _mutt_fmt_smime_command,
260 (unsigned long) cctx, 0);
261 debug_print (2, ("%s\n", d));
264 static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
265 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
266 int smimeerrfd, const char *fname,
267 const char *sig_fname, const char *cryptalg,
268 const char *key, const char *certificates,
269 const char *intermediates, const char *format)
271 struct smime_command_context cctx;
272 char cmd[HUGE_STRING];
276 if (!format || !*format)
280 cctx.sig_fname = sig_fname;
282 cctx.cryptalg = cryptalg;
283 cctx.certificates = certificates;
284 cctx.intermediates = intermediates;
286 mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
288 return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
289 smimeinfd, smimeoutfd, smimeerrfd);
298 * Key and certificate handling.
304 Search the certificate index for given mailbox.
305 return certificate file name.
308 static void smime_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
310 smime_id *Table = (smime_id *) menu->data;
311 smime_id this = Table[num];
312 const char *truststate;
314 switch (this.trust) {
316 truststate = N_("Trusted ");
319 truststate = N_("Verified ");
322 truststate = N_("Unverified");
325 truststate = N_("Expired ");
328 truststate = N_("Revoked ");
331 truststate = N_("Invalid ");
334 truststate = N_("Unknown ");
337 snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
338 truststate, this.email, this.nick);
340 snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
341 this.email, this.nick);
348 char *smime_ask_for_key (char *prompt, char *mailbox __attribute__((unused)),
353 long cert_num; /* Will contain the number of certificates.
354 * To be able to get it, the .index file will be read twice... */
355 char index_file[_POSIX_PATH_MAX];
357 char buf[LONG_STRING];
358 char fields[5][STRING];
359 int numFields, hash_suffix, done, cur; /* The current entry */
362 char helpstr[HUGE_STRING * 3];
367 prompt = _("Enter keyID: ");
368 snprintf (index_file, sizeof (index_file), "%s/.index",
369 public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));
371 idx = fopen (index_file, "r");
373 mutt_perror (index_file);
378 while (!feof (idx)) {
379 if (fgets (buf, sizeof (buf), idx))
386 if (mutt_get_field (prompt, qry, sizeof (qry), 0))
388 snprintf (title, sizeof (title),
389 _("S/MIME certificates matching \"%s\"."), qry);
392 idx = fopen (index_file, "r");
394 mutt_perror (index_file);
399 Table = p_new(smime_id, cert_num);
400 while (!feof (idx)) {
402 fscanf (idx, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
403 fields[0], &hash, &hash_suffix, fields[2]);
405 fscanf (idx, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
406 fields[3], fields[4]);
408 /* 0=email 1=name 2=nick 3=intermediate 4=trust */
412 /* Check if query matches this certificate */
413 if (!m_stristr(fields[0], qry) && !m_stristr(fields[2], qry))
416 Table[cur].hash = hash;
417 Table[cur].suffix = hash_suffix;
418 m_strcpy(Table[cur].email, sizeof(Table[cur].email), fields[0]);
419 m_strcpy(Table[cur].nick, sizeof(Table[cur].nick), fields[2]);
420 Table[cur].trust = *fields[4];
421 Table[cur].public = public;
427 /* Make Helpstring */
429 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT);
430 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
431 mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME,
432 OP_GENERIC_SELECT_ENTRY);
433 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
434 mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
435 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
437 /* Create the menu */
438 menu = mutt_new_menu ();
440 menu->make_entry = smime_entry;
441 menu->menu = MENU_SMIME;
442 menu->help = helpstr;
445 /* sorting keys might be done later - TODO */
452 switch (mutt_menuLoop (menu)) {
453 case OP_GENERIC_SELECT_ENTRY:
465 fname = p_new(char, 13); /* Hash + '.' + Suffix + \0 */
466 sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
471 mutt_menuDestroy (&menu);
473 set_option (OPTNEEDREDRAW);
482 char *smime_get_field_from_db (char *mailbox, char *query, short public,
485 int addr_len, query_len, found = 0, ask = 0, choice = 0;
486 char cert_path[_POSIX_PATH_MAX];
487 char buf[LONG_STRING], prompt[STRING];
488 char fields[5][STRING];
492 char key_trust_level = 0;
495 if (!mailbox && !query)
498 addr_len = mailbox ? m_strlen(mailbox) : 0;
499 query_len = query ? m_strlen(query) : 0;
503 /* index-file format:
504 mailbox certfile label issuer_certfile trust_flags\n
506 certfile is a hash value generated by openssl.
507 Note that this was done according to the OpenSSL
508 specs on their CA-directory.
511 snprintf (cert_path, sizeof (cert_path), "%s/.index",
512 (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));
514 if (!stat (cert_path, &info)) {
515 if ((fp = safe_fopen (cert_path, "r")) == NULL) {
516 mutt_perror (cert_path);
520 while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
521 if (mailbox && !(m_strncasecmp(mailbox, buf, addr_len))) {
522 numFields = sscanf (buf,
523 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
524 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
525 MUTT_FORMAT (STRING) "\n",
526 fields[0], fields[1],
527 fields[2], fields[3], fields[4]);
530 if (mailbox && public &&
532 *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
536 if (public && *fields[4] == 'u')
537 snprintf (prompt, sizeof (prompt),
539 ("ID %s is unverified. Do you want to use it for %s ?"),
541 else if (public && *fields[4] == 'v')
542 snprintf (prompt, sizeof (prompt),
543 _("Use (untrusted!) ID %s for %s ?"),
546 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
550 if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
556 else if (choice == M_NO) {
560 else if (choice == M_YES) {
561 m_strcpy(key, sizeof(key), fields[1]);
568 key_trust_level = *fields[4];
569 m_strcpy(key, sizeof(key), fields[1]);
574 numFields = sscanf (buf,
575 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
576 MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
577 MUTT_FORMAT (STRING) "\n",
578 fields[0], fields[1],
579 fields[2], fields[3], fields[4]);
581 /* query = label: return certificate. */
582 if (numFields >= 3 &&
583 !(m_strncasecmp(query, fields[2], query_len))) {
585 m_strcpy(key, sizeof(key), fields[1]);
587 /* query = certificate: return intermediate certificate. */
588 else if (numFields >= 4 &&
589 !(m_strncasecmp(query, fields[1], query_len))) {
591 m_strcpy(key, sizeof(key), fields[3]);
598 if (public && *fields[4] == 'u')
599 snprintf (prompt, sizeof (prompt),
600 _("ID %s is unverified. Do you want to use it for %s ?"),
602 else if (public && *fields[4] == 'v')
603 snprintf (prompt, sizeof (prompt),
604 _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
606 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
608 choice = mutt_yesorno (prompt, M_NO);
609 if (choice == -1 || choice == M_NO)
612 else if (key_trust_level && may_ask) {
613 if (key_trust_level == 'u') {
614 snprintf (prompt, sizeof (prompt),
615 _("ID %s is unverified. Do you want to use it for %s ?"),
617 choice = mutt_yesorno (prompt, M_NO);
621 else if (key_trust_level == 'v') {
623 ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
631 /* Note: m_strdup("") returns NULL. */
632 return m_strdup(key);
639 This sets the '*ToUse' variables for an upcoming decryption, where
640 the reuquired key is different from SmimeDefaultKey.
643 void _smime_getkeys (char *mailbox)
648 k = smime_get_field_from_db (mailbox, NULL, 0, 1);
651 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
652 k = smime_ask_for_key (buf, mailbox, 0);
656 /* the key used last time. */
657 if (*SmimeKeyToUse &&
658 !m_strcasecmp(k, SmimeKeyToUse + m_strlen(SmimeKeys) + 1)) {
663 smime_void_passphrase ();
665 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
666 NONULL (SmimeKeys), k);
668 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
669 NONULL (SmimeCertificates), k);
671 if (m_strcasecmp(k, SmimeDefaultKey))
672 smime_void_passphrase ();
678 if (*SmimeKeyToUse) {
679 if (!m_strcasecmp(SmimeDefaultKey,
680 SmimeKeyToUse + m_strlen(SmimeKeys) + 1))
683 smime_void_passphrase ();
686 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
687 NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
689 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
690 NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
693 void smime_getkeys (ENVELOPE * env)
698 if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
699 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
700 NONULL (SmimeKeys), SmimeDefaultKey);
702 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
703 NONULL (SmimeCertificates), SmimeDefaultKey);
708 for (t = env->to; !found && t; t = t->next)
709 if (mutt_addr_is_user (t)) {
711 _smime_getkeys (t->mailbox);
713 for (t = env->cc; !found && t; t = t->next)
714 if (mutt_addr_is_user (t)) {
716 _smime_getkeys (t->mailbox);
718 if (!found && (t = mutt_default_from ())) {
719 _smime_getkeys (t->mailbox);
720 address_list_wipe(&t);
724 /* This routine attempts to find the keyids of the recipients of a message.
725 * It returns NULL if any of the keys can not be found.
728 char *smime_findKeys (address_t * to, address_t * cc, address_t * bcc)
730 char *keyID, *keylist = NULL;
731 ssize_t keylist_size = 0;
732 ssize_t keylist_used = 0;
733 address_t *tmp = NULL, *addr = NULL;
734 address_t **last = &tmp;
738 const char *fqdn = mutt_fqdn (1);
740 for (i = 0; i < 3; i++) {
755 *last = address_list_dup (p);
757 last = &((*last)->next);
761 rfc822_qualify (tmp, fqdn);
763 tmp = mutt_remove_duplicates (tmp);
765 for (p = tmp; p; p = p->next) {
766 char buf[LONG_STRING];
770 if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
771 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
772 keyID = smime_ask_for_key (buf, q->mailbox, 1);
775 mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
777 address_list_wipe(&tmp);
778 address_list_wipe(&addr);
782 keylist_size += m_strlen(keyID) + 2;
783 p_realloc(&keylist, keylist_size);
784 sprintf (keylist + keylist_used, "%s\n", keyID); /* __SPRINTF_CHECKED__ */
785 keylist_used = m_strlen(keylist);
787 address_list_wipe(&addr);
790 address_list_wipe(&tmp);
799 static int smime_handle_cert_email (char *certificate, char *mailbox,
800 int copy, char ***buffer, int *num)
802 FILE *fpout = NULL, *fperr = NULL;
803 char tmpfname[_POSIX_PATH_MAX];
805 int ret = -1, count = 0;
808 mutt_mktemp (tmpfname);
809 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
810 mutt_perror (tmpfname);
813 mutt_unlink (tmpfname);
815 mutt_mktemp (tmpfname);
816 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
818 mutt_perror (tmpfname);
821 mutt_unlink (tmpfname);
823 if ((thepid = smime_invoke (NULL, NULL, NULL,
824 -1, fileno (fpout), fileno (fperr),
825 certificate, NULL, NULL, NULL, NULL, NULL,
826 SmimeGetCertEmailCommand)) == -1) {
827 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
833 mutt_wait_filter (thepid);
841 while ((fgets (email, sizeof (email), fpout))) {
842 *(email + m_strlen(email) - 1) = '\0';
843 if (m_strncasecmp(email, mailbox, m_strlen(mailbox)) == 0)
846 ret = ret < 0 ? 0 : ret;
852 mutt_copy_stream (fperr, stdout);
853 mutt_any_key_to_continue (_
854 ("Error: unable to create OpenSSL subprocess!"));
862 if (copy && buffer && num) {
864 *buffer = p_new(char *, count);
868 while ((fgets (email, sizeof (email), fpout))) {
869 *(email + m_strlen(email) - 1) = '\0';
870 (*buffer)[count] = p_dupstr(email, m_strlen(email));
885 static char *smime_extract_certificate (char *infile)
887 FILE *fpout = NULL, *fperr = NULL;
888 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
889 char tmpfname[_POSIX_PATH_MAX];
894 mutt_mktemp (tmpfname);
895 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
896 mutt_perror (tmpfname);
899 mutt_unlink (tmpfname);
901 mutt_mktemp (pk7out);
902 if ((fpout = safe_fopen (pk7out, "w+")) == NULL) {
904 mutt_perror (pk7out);
908 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
909 extract the full set of certificates directly.
911 if ((thepid = smime_invoke (NULL, NULL, NULL,
912 -1, fileno (fpout), fileno (fperr),
913 infile, NULL, NULL, NULL, NULL, NULL,
914 SmimePk7outCommand)) == -1) {
915 mutt_any_key_to_continue (_
916 ("Error: unable to create OpenSSL subprocess!"));
919 mutt_unlink (pk7out);
923 mutt_wait_filter (thepid);
930 empty = (fgetc (fpout) == EOF);
932 mutt_perror (pk7out);
933 mutt_copy_stream (fperr, stdout);
936 mutt_unlink (pk7out);
943 mutt_mktemp (certfile);
944 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
946 mutt_unlink (pk7out);
947 mutt_perror (certfile);
951 /* Step 2: Extract the certificates from a PKCS#7 structure.
953 if ((thepid = smime_invoke (NULL, NULL, NULL,
954 -1, fileno (fpout), fileno (fperr),
955 pk7out, NULL, NULL, NULL, NULL, NULL,
956 SmimeGetCertCommand)) == -1) {
957 mutt_any_key_to_continue (_
958 ("Error: unable to create OpenSSL subprocess!"));
961 mutt_unlink (pk7out);
962 mutt_unlink (certfile);
966 mutt_wait_filter (thepid);
968 mutt_unlink (pk7out);
974 empty = (fgetc (fpout) == EOF);
976 mutt_copy_stream (fperr, stdout);
979 mutt_unlink (certfile);
986 return m_strdup(certfile);
989 static char *smime_extract_signer_certificate (char *infile)
991 FILE *fpout = NULL, *fperr = NULL;
992 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
993 char tmpfname[_POSIX_PATH_MAX];
998 mutt_mktemp (tmpfname);
999 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1000 mutt_perror (tmpfname);
1003 mutt_unlink (tmpfname);
1006 mutt_mktemp (certfile);
1007 if ((fpout = safe_fopen (certfile, "w+")) == NULL) {
1009 mutt_perror (certfile);
1013 /* Extract signer's certificate
1015 if ((thepid = smime_invoke (NULL, NULL, NULL,
1016 -1, -1, fileno (fperr),
1017 infile, NULL, NULL, NULL, certfile, NULL,
1018 SmimeGetSignerCertCommand)) == -1) {
1019 mutt_any_key_to_continue (_
1020 ("Error: unable to create OpenSSL subprocess!"));
1023 mutt_unlink (pk7out);
1024 mutt_unlink (certfile);
1028 mutt_wait_filter (thepid);
1034 empty = (fgetc (fpout) == EOF);
1037 mutt_copy_stream (fperr, stdout);
1038 mutt_any_key_to_continue (NULL);
1041 mutt_unlink (certfile);
1048 return m_strdup(certfile);
1054 /* Add a certificate and update index file (externally). */
1056 void smime_invoke_import (char *infile, char *mailbox __attribute__ ((unused)))
1058 char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1059 FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1062 mutt_mktemp (tmpfname);
1063 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL) {
1064 mutt_perror (tmpfname);
1067 mutt_unlink (tmpfname);
1069 mutt_mktemp (tmpfname);
1070 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL) {
1072 mutt_perror (tmpfname);
1075 mutt_unlink (tmpfname);
1079 if (option (OPTASKCERTLABEL))
1080 mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1083 if ((certfile = smime_extract_certificate (infile))) {
1086 if ((thepid = smime_invoke (&smimein, NULL, NULL,
1087 -1, fileno (fpout), fileno (fperr),
1088 certfile, NULL, NULL, NULL, NULL, NULL,
1089 SmimeImportCertCommand)) == -1) {
1090 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1093 fputs (buf, smimein);
1094 fputc ('\n', smimein);
1097 mutt_wait_filter (thepid);
1099 mutt_unlink (certfile);
1100 p_delete(&certfile);
1108 mutt_copy_stream (fpout, stdout);
1109 mutt_copy_stream (fperr, stdout);
1118 int smime_verify_sender (HEADER * h)
1120 char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1124 mutt_mktemp (tempfname);
1125 if (!(fpout = safe_fopen (tempfname, "w"))) {
1126 mutt_perror (tempfname);
1130 if (h->security & ENCRYPT)
1131 mutt_copy_message (fpout, Context, h,
1132 M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1133 CH_MIME | CH_WEED | CH_NONEWLINE);
1135 mutt_copy_message (fpout, Context, h, 0, 0);
1141 h->env->from = mutt_expand_aliases (h->env->from);
1142 mbox = h->env->from->mailbox;
1144 else if (h->env->sender) {
1145 h->env->sender = mutt_expand_aliases (h->env->sender);
1146 mbox = h->env->sender->mailbox;
1150 if ((certfile = smime_extract_signer_certificate (tempfname))) {
1151 mutt_unlink (tempfname);
1152 if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1154 mutt_any_key_to_continue (NULL);
1158 mutt_unlink (certfile);
1159 p_delete(&certfile);
1162 mutt_any_key_to_continue (_("no certfile"));
1165 mutt_any_key_to_continue (_("no mbox"));
1167 mutt_unlink (tempfname);
1180 * Creating S/MIME - bodies.
1187 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1188 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1189 int smimeerrfd, const char *fname,
1192 return smime_invoke (smimein, smimeout, smimeerr,
1193 smimeinfd, smimeoutfd, smimeerrfd,
1194 fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1195 SmimeEncryptCommand);
1200 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1201 int smimeinfd, int smimeoutfd, int smimeerrfd,
1204 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1205 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1206 SmimeCertToUse, SmimeIntermediateToUse,
1213 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1215 char buf[LONG_STRING], certfile[LONG_STRING];
1216 char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1217 char smimeinfile[_POSIX_PATH_MAX];
1218 char *cert_start = certlist, *cert_end = certlist;
1219 FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1224 mutt_mktemp (tempfile);
1225 if ((fpout = safe_fopen (tempfile, "w+")) == NULL) {
1226 mutt_perror (tempfile);
1230 mutt_mktemp (smimeerrfile);
1231 if ((smimeerr = safe_fopen (smimeerrfile, "w+")) == NULL) {
1232 mutt_perror (smimeerrfile);
1234 mutt_unlink (tempfile);
1237 mutt_unlink (smimeerrfile);
1239 mutt_mktemp (smimeinfile);
1240 if ((fptmp = safe_fopen (smimeinfile, "w+")) == NULL) {
1241 mutt_perror (smimeinfile);
1242 mutt_unlink (tempfile);
1250 int off = m_strlen(certfile);
1252 while (*++cert_end && *cert_end != '\n');
1256 snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1257 NONULL (SmimeCertificates), cert_start);
1259 cert_start = cert_end;
1263 /* write a MIME entity */
1264 mutt_write_mime_header (a, fptmp);
1265 fputc ('\n', fptmp);
1266 mutt_write_mime_body (a, fptmp);
1270 smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1271 fileno (fpout), fileno (smimeerr),
1272 smimeinfile, certfile)) == -1) {
1274 mutt_unlink (smimeinfile);
1275 mutt_unlink (certfile);
1281 mutt_wait_filter (thepid);
1282 mutt_unlink (smimeinfile);
1283 mutt_unlink (certfile);
1287 empty = (fgetc (fpout) == EOF);
1292 while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1294 fputs (buf, stdout);
1298 /* pause if there is any error output from SMIME */
1300 mutt_any_key_to_continue (NULL);
1303 /* fatal error while trying to encrypt message */
1305 mutt_any_key_to_continue _("No output from OpenSSL..");
1307 mutt_unlink (tempfile);
1311 t = mutt_new_body ();
1312 t->type = TYPEAPPLICATION;
1313 t->subtype = m_strdup("x-pkcs7-mime");
1314 mutt_set_parameter ("name", "smime.p7m", &t->parameter);
1315 mutt_set_parameter ("smime-type", "enveloped-data", &t->parameter);
1316 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
1318 t->disposition = DISPATTACH;
1319 t->d_filename = m_strdup("smime.p7m");
1320 t->filename = m_strdup(tempfile);
1321 t->unlink = 1; /*delete after sending the message */
1331 BODY *smime_sign_message (BODY * a)
1334 char buffer[LONG_STRING];
1335 char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1336 FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1340 char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1342 if (!intermediates) {
1343 mutt_message (_("Warning: Intermediate certificate not found."));
1344 intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
1347 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
1349 mutt_mktemp (filetosign);
1350 if ((sfp = safe_fopen (filetosign, "w+")) == NULL) {
1351 mutt_perror (filetosign);
1355 mutt_mktemp (signedfile);
1356 if ((smimeout = safe_fopen (signedfile, "w+")) == NULL) {
1357 mutt_perror (signedfile);
1359 mutt_unlink (filetosign);
1363 mutt_write_mime_header (a, sfp);
1365 mutt_write_mime_body (a, sfp);
1370 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1371 NONULL (SmimeKeys), SmimeDefaultKey);
1373 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1374 NONULL (SmimeCertificates), SmimeDefaultKey);
1376 snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1377 NONULL (SmimeCertificates), intermediates);
1381 if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1382 -1, fileno (smimeout), -1,
1383 filetosign)) == -1) {
1384 mutt_perror (_("Can't open OpenSSL subprocess!"));
1387 mutt_unlink (signedfile);
1388 mutt_unlink (filetosign);
1391 fputs (SmimePass, smimein);
1392 fputc ('\n', smimein);
1396 mutt_wait_filter (thepid);
1398 /* check for errors from OpenSSL */
1402 while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1404 fputs (buffer, stdout);
1411 empty = (fgetc (smimeout) == EOF);
1414 mutt_unlink (filetosign);
1418 mutt_any_key_to_continue (NULL);
1421 mutt_any_key_to_continue _("No output from OpenSSL...");
1423 mutt_unlink (signedfile);
1424 return (NULL); /* fatal error while signing */
1427 t = mutt_new_body ();
1428 t->type = TYPEMULTIPART;
1429 t->subtype = m_strdup("signed");
1430 t->encoding = ENC7BIT;
1432 t->disposition = DISPINLINE;
1434 mutt_generate_boundary (&t->parameter);
1435 /* check if this can be extracted from private key somehow.... */
1436 mutt_set_parameter ("micalg", "sha1", &t->parameter);
1437 mutt_set_parameter ("protocol", "application/x-pkcs7-signature",
1443 t->parts->next = mutt_new_body ();
1445 t->type = TYPEAPPLICATION;
1446 t->subtype = m_strdup("x-pkcs7-signature");
1447 t->filename = m_strdup(signedfile);
1448 t->d_filename = m_strdup("smime.p7s");
1450 t->disposition = DISPATTACH;
1451 t->encoding = ENCBASE64;
1452 t->unlink = 1; /* ok to remove this file after sending. */
1464 * Handling S/MIME - bodies.
1473 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1474 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1475 int smimeerrfd, const char *fname,
1476 const char *sig_fname, int opaque)
1478 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1479 smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1480 (opaque ? SmimeVerifyOpaqueCommand :
1481 SmimeVerifyCommand));
1486 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1487 FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1488 int smimeerrfd, const char *fname)
1490 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1491 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1492 SmimeCertToUse, NULL, SmimeDecryptCommand);
1497 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1499 char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1500 FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1505 ssize_t tmplength = 0;
1506 int origType = sigbdy->type;
1507 char *savePrefix = NULL;
1510 snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1512 /* decode to a tempfile, saving the original destination */
1514 if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1515 mutt_perror (signedfile);
1518 /* decoding the attachment changes the size and offset, so save a copy
1519 * of the "real" values now, and restore them after processing
1521 tmplength = sigbdy->length;
1522 tmpoffset = sigbdy->offset;
1524 /* if we are decoding binary bodies, we don't want to prefix each
1525 * line with the prefix or else the data will get corrupted.
1527 savePrefix = s->prefix;
1530 mutt_decode_attachment (sigbdy, s);
1532 sigbdy->length = ftello (s->fpout);
1536 /* restore final destination and substitute the tempfile for input */
1539 s->fpin = fopen (signedfile, "r");
1541 /* restore the prefix */
1542 s->prefix = savePrefix;
1544 sigbdy->type = origType;
1547 mutt_mktemp (smimeerrfile);
1548 if (!(smimeerr = safe_fopen (smimeerrfile, "w+"))) {
1549 mutt_perror (smimeerrfile);
1550 mutt_unlink (signedfile);
1554 crypt_current_time (s, "OpenSSL");
1556 if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1557 -1, -1, fileno (smimeerr),
1558 tempfile, signedfile, 0)) != -1) {
1562 if (mutt_wait_filter (thepid))
1572 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1573 if (linelen && !m_strcasecmp(line, "verification successful"))
1582 mutt_copy_stream (smimeerr, s->fpout);
1585 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1587 mutt_unlink (signedfile);
1588 mutt_unlink (smimeerrfile);
1590 sigbdy->length = tmplength;
1591 sigbdy->offset = tmpoffset;
1593 /* restore the original source stream */
1606 This handles application/pkcs7-mime which can either be a signed
1607 or an encrypted message.
1610 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1615 char buf[HUGE_STRING];
1616 char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1617 char tmpfname[_POSIX_PATH_MAX];
1618 char tmptmpfname[_POSIX_PATH_MAX];
1619 FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1620 FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1624 unsigned int type = mutt_is_application_smime (m);
1626 if (!(type & APPLICATION_SMIME))
1629 mutt_mktemp (outfile);
1630 if ((smimeout = safe_fopen (outfile, "w+")) == NULL) {
1631 mutt_perror (outfile);
1635 mutt_mktemp (errfile);
1636 if ((smimeerr = safe_fopen (errfile, "w+")) == NULL) {
1637 mutt_perror (errfile);
1642 mutt_unlink (errfile);
1645 mutt_mktemp (tmpfname);
1646 if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL) {
1647 mutt_perror (tmpfname);
1655 fseeko (s->fpin, m->offset, 0);
1656 last_pos = m->offset;
1658 mutt_copy_bytes (s->fpin, tmpfp, m->length);
1663 if ((type & ENCRYPT) &&
1664 (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1665 fileno (smimeout), fileno (smimeerr),
1669 mutt_unlink (tmpfname);
1670 if (s->flags & M_DISPLAY)
1671 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
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 if (s->flags & M_DISPLAY)
1683 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1688 if (type & ENCRYPT) {
1689 if (!smime_valid_passphrase ())
1690 smime_void_passphrase ();
1691 fputs (SmimePass, smimein);
1692 fputc ('\n', smimein);
1697 mutt_wait_filter (thepid);
1698 mutt_unlink (tmpfname);
1701 if (s->flags & M_DISPLAY) {
1704 if ((c = fgetc (smimeerr)) != EOF) {
1705 ungetc (c, smimeerr);
1707 crypt_current_time (s, "OpenSSL");
1708 mutt_copy_stream (smimeerr, s->fpout);
1709 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1713 state_attach_puts (_("[-- The following data is S/MIME"
1714 " encrypted --]\n"), s);
1716 state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1727 mutt_mktemp (tmptmpfname);
1728 if ((fpout = safe_fopen (tmptmpfname, "w+")) == NULL) {
1729 mutt_perror (tmptmpfname);
1735 while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1736 len = m_strlen(buf);
1737 if (len > 1 && buf[len - 2] == '\r') {
1738 buf[len - 2] = '\n';
1739 buf[len - 1] = '\0';
1747 if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1748 fstat (fileno (fpout), &info);
1749 p->length = info.st_size - p->offset;
1751 mutt_parse_part (fpout, p);
1754 tmpfp_buffer = s->fpin;
1756 mutt_body_handler (p, s);
1757 s->fpin = tmpfp_buffer;
1763 mutt_unlink (outfile);
1767 mutt_unlink (tmptmpfname);
1772 if (s->flags & M_DISPLAY) {
1774 state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1776 state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1779 if (type & SIGNOPAQUE) {
1786 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1787 if (linelen && !m_strcasecmp(line, "verification successful"))
1792 m->goodsig = p->goodsig;
1793 m->badsig = p->badsig;
1804 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1808 char tempfile[_POSIX_PATH_MAX];
1810 long tmpoffset = b->offset;
1811 ssize_t tmplength = b->length;
1812 int origType = b->type;
1816 if (!mutt_is_application_smime (b))
1824 fseeko (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 = ftello (s.fpout);
1842 mutt_mktemp (tempfile);
1843 if ((*fpout = safe_fopen (tempfile, "w+")) == NULL) {
1844 mutt_perror (tempfile);
1848 mutt_unlink (tempfile);
1850 if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1855 (*cur)->goodsig = b->goodsig;
1856 (*cur)->badsig = b->badsig;
1860 b->length = tmplength;
1861 b->offset = tmpoffset;
1863 safe_fclose (&tmpfp);
1870 int smime_application_smime_handler (BODY * m, STATE * s)
1872 return smime_handle_entity (m, s, NULL) ? 0 : -1;
1875 int smime_send_menu (HEADER * msg, int *redraw)
1879 switch (mutt_multi_choice
1880 (_("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1882 case 1: /* (e)ncrypt */
1883 msg->security |= ENCRYPT;
1884 msg->security &= ~SIGN;
1887 case 3: /* encrypt (w)ith */
1890 msg->security |= ENCRYPT;
1893 /* I use "dra" because "123" is recognized anyway */
1894 switch (mutt_multi_choice (_("Choose algorithm family:"
1895 " 1: DES, 2: RC2, 3: AES,"
1896 " or (c)lear? "), _("drac"))) {
1898 switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
1901 m_strreplace(&SmimeCryptAlg, "des");
1904 m_strreplace(&SmimeCryptAlg, "des3");
1910 switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
1913 m_strreplace(&SmimeCryptAlg, "rc2-40");
1916 m_strreplace(&SmimeCryptAlg, "rc2-64");
1919 m_strreplace(&SmimeCryptAlg, "rc2-128");
1925 switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
1928 m_strreplace(&SmimeCryptAlg, "aes128");
1931 m_strreplace(&SmimeCryptAlg, "aes192");
1934 m_strreplace(&SmimeCryptAlg, "aes256");
1939 case 4: /* (c)lear */
1940 p_delete(&SmimeCryptAlg);
1942 case -1: /* Ctrl-G or Enter */
1946 } while (choice == -1);
1950 case 2: /* (s)ign */
1952 if (!SmimeDefaultKey)
1953 mutt_message (_("Can't sign: No key specified. Use Sign As."));
1956 msg->security |= SIGN;
1957 msg->security &= ~ENCRYPT;
1961 case 4: /* sign (a)s */
1963 if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1964 m_strreplace(&SmimeDefaultKey, p);
1966 msg->security |= SIGN;
1968 /* probably need a different passphrase */
1969 crypt_smime_void_passphrase ();
1973 msg->security &= ~SIGN;
1976 *redraw = REDRAW_FULL;
1979 case 5: /* (b)oth */
1980 msg->security |= (ENCRYPT | SIGN);
1983 case 6: /* (f)orget it */
1984 case 7: /* (c)lear */
1989 if (msg->security && msg->security != APPLICATION_SMIME)
1990 msg->security |= APPLICATION_SMIME;
1994 return (msg->security);