2 * Copyright (C) 2001,2002 Oliver Ehli <elmy@acm.org>
3 * Copyright (C) 2002 Mike Schiraldi <raldi@research.netsol.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
21 #include "mutt_curses.h"
22 #include "mutt_menu.h"
39 #ifdef HAVE_SYS_TIME_H
40 # include <sys/time.h>
43 #ifdef HAVE_SYS_RESOURCE_H
44 # include <sys/resource.h>
47 #ifdef CRYPT_BACKEND_CLASSIC_SMIME
49 #include "mutt_crypt.h"
52 struct smime_command_context {
53 const char *key; /* %k */
54 const char *cryptalg; /* %a */
55 const char *fname; /* %f */
56 const char *sig_fname; /* %s */
57 const char *certificates; /* %c */
58 const char *intermediates; /* %i */
67 char trust; /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
68 short public; /* 1=public 0=private */
72 char SmimePass[STRING];
73 time_t SmimeExptime = 0; /* when does the cached passphrase expire? */
76 static char SmimeKeyToUse[_POSIX_PATH_MAX] = { 0 };
77 static char SmimeCertToUse[_POSIX_PATH_MAX];
78 static char SmimeIntermediateToUse[_POSIX_PATH_MAX];
82 * Queries and passphrase handling.
88 /* these are copies from pgp.c */
91 void smime_void_passphrase (void)
93 memset (SmimePass, 0, sizeof (SmimePass));
103 * The OpenSSL interface
106 /* This is almost identical to ppgp's invoking interface. */
108 static const char *_mutt_fmt_smime_command (char *dest,
113 const char *ifstring,
114 const char *elsestring,
119 struct smime_command_context *cctx = (struct smime_command_context *) data;
120 int optional = (flags & M_FORMAT_OPTIONAL);
128 char path[_POSIX_PATH_MAX];
129 char buf1[LONG_STRING], buf2[LONG_STRING];
132 strfcpy (path, NONULL (SmimeCALocation), sizeof (path));
133 mutt_expand_path (path, sizeof (path));
134 mutt_quote_filename (buf1, sizeof (buf1), path);
136 if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
137 snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
139 snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
141 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
142 snprintf (dest, destlen, fmt, buf2);
144 else if (!SmimeCALocation)
150 { /* certificate (list) */
152 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
153 snprintf (dest, destlen, fmt, NONULL(cctx->certificates));
155 else if (!cctx->certificates)
161 { /* intermediate certificates */
163 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
164 snprintf (dest, destlen, fmt, NONULL(cctx->intermediates));
166 else if (!cctx->intermediates)
172 { /* detached signature */
175 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
176 snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
178 else if (!cctx->sig_fname)
187 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
188 snprintf (dest, destlen, fmt, NONULL (cctx->key));
196 { /* algorithm for encryption */
198 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
199 snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
207 { /* file to process */
210 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
211 snprintf (dest, destlen, fmt, NONULL (cctx->fname));
213 else if (!cctx->fname)
224 mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
226 else if (flags & M_FORMAT_OPTIONAL)
227 mutt_FormatString (dest, destlen, elsestring, _mutt_fmt_smime_command,
235 static void mutt_smime_command (char *d, size_t dlen,
236 struct smime_command_context *cctx, const char *fmt)
238 mutt_FormatString (d, dlen, NONULL(fmt), _mutt_fmt_smime_command,
239 (unsigned long) cctx, 0);
240 dprint (2,(debugfile, "mutt_smime_command: %s\n", d));
246 static pid_t smime_invoke (FILE **smimein, FILE **smimeout, FILE **smimeerr,
247 int smimeinfd, int smimeoutfd, int smimeerrfd,
249 const char *sig_fname,
250 const char *cryptalg,
252 const char *certificates,
253 const char *intermediates,
256 struct smime_command_context cctx;
257 char cmd[HUGE_STRING];
259 memset (&cctx, 0, sizeof (cctx));
261 if (!format || !*format)
265 cctx.sig_fname = sig_fname;
267 cctx.cryptalg = cryptalg;
268 cctx.certificates = certificates;
269 cctx.intermediates = intermediates;
271 mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
273 return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
274 smimeinfd, smimeoutfd, smimeerrfd);
283 * Key and certificate handling.
289 Search the certificate index for given mailbox.
290 return certificate file name.
293 static void smime_entry (char *s, size_t l, MUTTMENU * menu, int num)
295 smime_id *Table = (smime_id*) menu->data;
296 smime_id this = Table[num];
300 truststate = N_("Trusted ");
303 truststate = N_("Verified ");
306 truststate = N_("Unverified");
309 truststate = N_("Expired ");
312 truststate = N_("Revoked ");
315 truststate = N_("Invalid ");
318 truststate = N_("Unknown ");
321 snprintf(s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix, truststate, this.email, this.nick);
323 snprintf(s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix, this.email, this.nick);
330 char* smime_ask_for_key (char *prompt, char *mailbox, short public)
334 long cert_num; /* Will contain the number of certificates.
335 * To be able to get it, the .index file will be read twice... */
336 char index_file[_POSIX_PATH_MAX];
338 char buf[LONG_STRING];
339 char fields[5][STRING];
340 int numFields, hash_suffix, done, cur; /* The current entry */
343 char helpstr[HUGE_STRING*3];
347 if (!prompt) prompt = _("Enter keyID: ");
348 snprintf(index_file, sizeof (index_file), "%s/.index",
349 public ? NONULL(SmimeCertificates) : NONULL(SmimeKeys));
351 index = fopen(index_file, "r");
354 mutt_perror (index_file);
359 while (!feof(index)) {
360 if (fgets(buf, sizeof(buf), index)) cert_num++;
367 if (mutt_get_field(prompt,
368 qry, sizeof(qry), 0))
370 snprintf(title, sizeof(title), _("S/MIME certificates matching \"%s\"."),
374 index = fopen(index_file, "r");
377 mutt_perror (index_file);
382 Table = safe_calloc(cert_num, sizeof (smime_id));
383 while (!feof(index)) {
384 numFields = fscanf (index, MUTT_FORMAT(STRING) " %x.%i " MUTT_FORMAT(STRING), fields[0], &hash,
385 &hash_suffix, fields[2]);
387 fscanf (index, MUTT_FORMAT(STRING) " " MUTT_FORMAT(STRING) "\n", fields[3], fields[4]);
389 /* 0=email 1=name 2=nick 3=intermediate 4=trust */
390 if (numFields < 2) continue;
392 /* Check if query matches this certificate */
393 if (!mutt_stristr(fields[0], qry) &&
394 !mutt_stristr(fields[2], qry))
397 Table[cur].hash = hash;
398 Table[cur].suffix = hash_suffix;
399 strncpy(Table[cur].email, fields[0], sizeof(Table[cur].email));
400 strncpy(Table[cur].nick, fields[2], sizeof(Table[cur].nick));
401 Table[cur].trust = *fields[4];
402 Table[cur].public = public;
408 /* Make Helpstring */
410 mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_SMIME, OP_EXIT);
411 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
412 mutt_make_help (buf, sizeof (buf), _("Select "), MENU_SMIME,
413 OP_GENERIC_SELECT_ENTRY);
414 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
415 mutt_make_help (buf, sizeof(buf), _("Help"), MENU_SMIME, OP_HELP);
416 strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
418 /* Create the menu */
419 menu = mutt_new_menu();
421 menu->make_entry = smime_entry;
422 menu->menu = MENU_SMIME;
423 menu->help = helpstr;
426 /* sorting keys might be done later - TODO */
433 switch (mutt_menuLoop (menu)) {
434 case OP_GENERIC_SELECT_ENTRY:
446 fname = safe_malloc(14); /* Hash + '.' + Suffix + \n + \0 */
447 sprintf(fname, "%.8x.%i\n", Table[cur].hash, Table[cur].suffix);
451 mutt_menuDestroy (&menu);
453 set_option (OPTNEEDREDRAW);
455 if (fname) return fname;
461 char *smime_get_field_from_db (char *mailbox, char *query, short public, short may_ask)
463 int addr_len, query_len, found = 0, ask = 0, choice = 0;
464 char cert_path[_POSIX_PATH_MAX];
465 char buf[LONG_STRING], prompt[STRING];
466 char fields[5][STRING];
469 char *key=NULL, key_trust_level = 0;
472 if(!mailbox && !query) return(NULL);
474 addr_len = mailbox ? mutt_strlen (mailbox) : 0;
475 query_len = query ? mutt_strlen (query) : 0;
477 /* index-file format:
478 mailbox certfile label issuer_certfile trust_flags\n
480 \n is also copied here, serving as delimitation.
482 certfile is a hash value generated by openssl.
483 Note that this was done according to the OpenSSL
484 specs on their CA-directory.
487 snprintf (cert_path, sizeof (cert_path), "%s/.index",
488 (public ? NONULL(SmimeCertificates) : NONULL(SmimeKeys)));
490 if (!stat (cert_path, &info))
492 if ((fp = safe_fopen (cert_path, "r")) == NULL)
494 mutt_perror (cert_path);
498 while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
499 if (mailbox && !(mutt_strncasecmp (mailbox, buf, addr_len)))
501 numFields = sscanf (buf,
502 MUTT_FORMAT(STRING) " " MUTT_FORMAT(STRING) " "
503 MUTT_FORMAT(STRING) " " MUTT_FORMAT(STRING) " "
504 MUTT_FORMAT(STRING) "\n",
505 fields[0], fields[1],
506 fields[2], fields[3],
510 if (mailbox && public &&
512 *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
517 if (public && *fields[4] == 'u' )
518 snprintf (prompt, sizeof (prompt),
519 _("ID %s is unverified. Do you want to use it for %s ?"),
521 else if (public && *fields[4] == 'v' )
522 snprintf (prompt, sizeof (prompt),
523 _("Use (untrusted!) ID %s for %s ?"),
526 snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
530 if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1)
538 else if (choice == M_NO)
543 else if (choice == M_YES)
545 snprintf (key,mutt_strlen(key)+1, "%s", fields[1]);
552 key = safe_calloc(1, mutt_strlen(fields[1])+2);
553 if (public) key_trust_level = *fields[4];
554 snprintf(key, mutt_strlen(fields[1])+1, "%s", fields[1]);
561 numFields = sscanf (buf, "%s %s %s %s %s\n", fields[0], fields[1],
562 fields[2], fields[3], fields[4]);
564 /* query = label: return certificate. */
565 if (numFields >= 3 &&
566 !(mutt_strncasecmp (query, fields[2], query_len)))
569 key = safe_calloc(1, mutt_strlen(fields[1])+2);
570 snprintf(key, mutt_strlen(fields[1])+1, "%s", fields[1]);
572 /* query = certificate: return intermediate certificate. */
573 else if (numFields >= 4 &&
574 !(mutt_strncasecmp (query, fields[1], query_len)))
577 key = safe_calloc(1, mutt_strlen(fields[3])+2);
578 snprintf(key, mutt_strlen(fields[3])+1, "%s", fields[3]);
586 if (public && *fields[4] == 'u' )
587 snprintf (prompt, sizeof (prompt),
588 _("ID %s is unverified. Do you want to use it for %s ?"),
590 else if (public && *fields[4] == 'v' )
591 snprintf (prompt, sizeof (prompt),
592 _("Use (untrusted!) ID %s for %s ?"),
595 snprintf (prompt, sizeof(prompt), _("Use ID %s for %s ?"), key,
597 choice = mutt_yesorno (prompt, M_NO);
598 if (choice == -1 || choice == M_NO)
604 else if (key_trust_level && may_ask)
606 if (key_trust_level == 'u' )
608 snprintf (prompt, sizeof (prompt),
609 _("ID %s is unverified. Do you want to use it for %s ?"),
611 choice = mutt_yesorno (prompt, M_NO);
619 else if (key_trust_level == 'v' )
621 mutt_error (_("Warning: You have not yet decided to trust ID %s. (any key to continue)"), key);
630 key[mutt_strlen(key)+1] = '\0';
631 key[mutt_strlen(key)] = '\n';
641 This sets the '*ToUse' variables for an upcoming decryption, where
642 the reuquired key is different from SmimeDefaultKey.
645 void _smime_getkeys (char *mailbox)
650 k = smime_get_field_from_db (mailbox, NULL, 0, 1);
654 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "),
656 k = smime_ask_for_key(buf, mailbox, 0);
661 k[mutt_strlen (k)-1] = '\0';
663 /* the key used last time. */
664 if (*SmimeKeyToUse &&
665 !mutt_strcasecmp (k, SmimeKeyToUse + mutt_strlen (SmimeKeys)+1))
670 else smime_void_passphrase ();
672 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
673 NONULL(SmimeKeys), k);
675 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
676 NONULL(SmimeCertificates), k);
678 if (mutt_strcasecmp (k, SmimeDefaultKey))
679 smime_void_passphrase ();
687 if (!mutt_strcasecmp (SmimeDefaultKey,
688 SmimeKeyToUse + mutt_strlen (SmimeKeys)+1))
691 smime_void_passphrase ();
694 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
695 NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
697 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
698 NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
701 void smime_getkeys (ENVELOPE *env)
706 if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey)
708 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
709 NONULL (SmimeKeys), SmimeDefaultKey);
711 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
712 NONULL(SmimeCertificates), SmimeDefaultKey);
717 for (t = env->to; !found && t; t = t->next)
718 if (mutt_addr_is_user (t))
721 _smime_getkeys (t->mailbox);
723 for (t = env->cc; !found && t; t = t->next)
724 if (mutt_addr_is_user (t))
727 _smime_getkeys (t->mailbox);
729 if (!found && (t = mutt_default_from()))
731 _smime_getkeys (t->mailbox);
732 rfc822_free_address (&t);
736 /* This routine attempts to find the keyids of the recipients of a message.
737 * It returns NULL if any of the keys can not be found.
740 char *smime_findKeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc)
742 char *keyID, *keylist = NULL;
743 size_t keylist_size = 0;
744 size_t keylist_used = 0;
745 ADDRESS *tmp = NULL, *addr = NULL;
746 ADDRESS **last = &tmp;
750 const char *fqdn = mutt_fqdn (1);
752 for (i = 0; i < 3; i++)
756 case 0: p = to; break;
757 case 1: p = cc; break;
758 case 2: p = bcc; break;
762 *last = rfc822_cpy_adr (p);
764 last = &((*last)->next);
768 rfc822_qualify (tmp, fqdn);
770 tmp = mutt_remove_duplicates (tmp);
772 for (p = tmp; p ; p = p->next)
774 char buf[LONG_STRING];
778 if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL)
780 snprintf(buf, sizeof(buf),
781 _("Enter keyID for %s: "),
783 keyID = smime_ask_for_key(buf, q->mailbox, 1);
787 mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
789 rfc822_free_address (&tmp);
790 rfc822_free_address (&addr);
794 keylist_size += mutt_strlen (keyID) + 1;
795 safe_realloc (&keylist, keylist_size);
796 sprintf (keylist + keylist_used, "%s", keyID); /* __SPRINTF_CHECKED__ */
797 keylist_used = mutt_strlen (keylist);
799 rfc822_free_address (&addr);
802 rfc822_free_address (&tmp);
811 static int smime_handle_cert_email (char *certificate, char *mailbox,
812 int copy, char ***buffer, int *num)
814 FILE *fpout = NULL, *fperr = NULL;
815 char tmpfname[_POSIX_PATH_MAX];
817 int ret = -1, count = 0;
820 mutt_mktemp (tmpfname);
821 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL)
823 mutt_perror (tmpfname);
826 mutt_unlink (tmpfname);
828 mutt_mktemp (tmpfname);
829 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL)
832 mutt_perror (tmpfname);
835 mutt_unlink (tmpfname);
837 if ((thepid = smime_invoke (NULL, NULL, NULL,
838 -1, fileno (fpout), fileno (fperr),
839 certificate, NULL, NULL, NULL, NULL, NULL,
840 SmimeGetCertEmailCommand))== -1)
842 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
848 mutt_wait_filter (thepid);
856 while ((fgets (email, sizeof (email), fpout)))
858 *(email + mutt_strlen (email)-1) = '\0';
859 if(mutt_strncasecmp (email, mailbox, mutt_strlen (mailbox)) == 0)
862 ret = ret < 0 ? 0 : ret;
869 mutt_copy_stream (fperr, stdout);
870 mutt_any_key_to_continue (_("Error: unable to create OpenSSL subprocess!"));
877 if(copy && buffer && num)
880 *buffer = safe_calloc(sizeof(char*), count);
884 while ((fgets (email, sizeof (email), fpout)))
886 *(email + mutt_strlen (email) - 1) = '\0';
887 (*buffer)[count] = safe_calloc(1, mutt_strlen (email) + 1);
888 strncpy((*buffer)[count], email, mutt_strlen (email));
892 else if(copy) ret = 2;
902 static char *smime_extract_certificate (char *infile)
904 FILE *fpout = NULL, *fperr = NULL;
905 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
906 char tmpfname[_POSIX_PATH_MAX];
911 mutt_mktemp (tmpfname);
912 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL)
914 mutt_perror (tmpfname);
917 mutt_unlink (tmpfname);
919 mutt_mktemp (pk7out);
920 if ((fpout = safe_fopen (pk7out, "w+")) == NULL)
923 mutt_perror (pk7out);
927 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
928 extract the full set of certificates directly.
930 if ((thepid = smime_invoke (NULL, NULL, NULL,
931 -1, fileno (fpout), fileno (fperr),
932 infile, NULL, NULL, NULL, NULL, NULL,
933 SmimePk7outCommand))== -1)
935 mutt_any_key_to_continue (_("Error: unable to create OpenSSL subprocess!"));
938 mutt_unlink (pk7out);
942 mutt_wait_filter (thepid);
949 empty = (fgetc (fpout) == EOF);
952 mutt_perror (pk7out);
953 mutt_copy_stream (fperr, stdout);
956 mutt_unlink (pk7out);
963 mutt_mktemp (certfile);
964 if ((fpout = safe_fopen (certfile, "w+")) == NULL)
967 mutt_unlink (pk7out);
968 mutt_perror (certfile);
972 /* Step 2: Extract the certificates from a PKCS#7 structure.
974 if ((thepid = smime_invoke (NULL, NULL, NULL,
975 -1, fileno (fpout), fileno (fperr),
976 pk7out, NULL, NULL, NULL, NULL, NULL,
977 SmimeGetCertCommand))== -1)
979 mutt_any_key_to_continue (_("Error: unable to create OpenSSL subprocess!"));
982 mutt_unlink (pk7out);
983 mutt_unlink (certfile);
987 mutt_wait_filter (thepid);
989 mutt_unlink (pk7out);
995 empty = (fgetc (fpout) == EOF);
998 mutt_copy_stream (fperr, stdout);
1001 mutt_unlink (certfile);
1008 return safe_strdup (certfile);
1011 static char *smime_extract_signer_certificate (char *infile)
1013 FILE *fpout = NULL, *fperr = NULL;
1014 char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
1015 char tmpfname[_POSIX_PATH_MAX];
1020 mutt_mktemp (tmpfname);
1021 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL)
1023 mutt_perror (tmpfname);
1026 mutt_unlink (tmpfname);
1029 mutt_mktemp (certfile);
1030 if ((fpout = safe_fopen (certfile, "w+")) == NULL)
1033 mutt_perror (certfile);
1037 /* Extract signer's certificate
1039 if ((thepid = smime_invoke (NULL, NULL, NULL,
1040 -1, -1, fileno (fperr),
1041 infile, NULL, NULL, NULL, certfile, NULL,
1042 SmimeGetSignerCertCommand))== -1)
1044 mutt_any_key_to_continue (_("Error: unable to create OpenSSL subprocess!"));
1047 mutt_unlink (pk7out);
1048 mutt_unlink (certfile);
1052 mutt_wait_filter (thepid);
1058 empty = (fgetc (fpout) == EOF);
1062 mutt_copy_stream (fperr, stdout);
1063 mutt_any_key_to_continue (NULL);
1066 mutt_unlink (certfile);
1073 return safe_strdup (certfile);
1079 /* Add a certificate and update index file (externally). */
1081 void smime_invoke_import (char *infile, char *mailbox)
1083 char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1084 FILE *smimein=NULL, *fpout = NULL, *fperr = NULL;
1087 mutt_mktemp (tmpfname);
1088 if ((fperr = safe_fopen (tmpfname, "w+")) == NULL)
1090 mutt_perror (tmpfname);
1093 mutt_unlink (tmpfname);
1095 mutt_mktemp (tmpfname);
1096 if ((fpout = safe_fopen (tmpfname, "w+")) == NULL)
1099 mutt_perror (tmpfname);
1102 mutt_unlink (tmpfname);
1106 if (option (OPTASKCERTLABEL))
1107 mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1110 if ((certfile = smime_extract_certificate(infile)))
1114 if ((thepid = smime_invoke (&smimein, NULL, NULL,
1115 -1, fileno(fpout), fileno(fperr),
1116 certfile, NULL, NULL, NULL, NULL, NULL,
1117 SmimeImportCertCommand))== -1)
1119 mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1122 fputs (buf, smimein);
1123 fputc ('\n', smimein);
1126 mutt_wait_filter (thepid);
1128 mutt_unlink (certfile);
1137 mutt_copy_stream (fpout, stdout);
1138 mutt_copy_stream (fperr, stdout);
1147 int smime_verify_sender(HEADER *h)
1149 char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1153 mutt_mktemp (tempfname);
1154 if (!(fpout = safe_fopen (tempfname, "w")))
1156 mutt_perror (tempfname);
1160 if(h->security & ENCRYPT)
1161 mutt_copy_message (fpout, Context, h,
1162 M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1163 CH_MIME|CH_WEED|CH_NONEWLINE);
1165 mutt_copy_message (fpout, Context, h, 0, 0);
1172 h->env->from = mutt_expand_aliases (h->env->from);
1173 mbox = h->env->from->mailbox;
1175 else if (h->env->sender)
1177 h->env->sender = mutt_expand_aliases (h->env->sender);
1178 mbox = h->env->sender->mailbox;
1183 if ((certfile = smime_extract_signer_certificate(tempfname)))
1185 mutt_unlink(tempfname);
1186 if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL))
1189 mutt_any_key_to_continue(NULL);
1193 mutt_unlink(certfile);
1197 mutt_any_key_to_continue(_("no certfile"));
1200 mutt_any_key_to_continue(_("no mbox"));
1202 mutt_unlink(tempfname);
1215 * Creating S/MIME - bodies.
1222 pid_t smime_invoke_encrypt (FILE **smimein, FILE **smimeout, FILE **smimeerr,
1223 int smimeinfd, int smimeoutfd, int smimeerrfd,
1224 const char *fname, const char *uids)
1226 return smime_invoke (smimein, smimeout, smimeerr,
1227 smimeinfd, smimeoutfd, smimeerrfd,
1228 fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1229 SmimeEncryptCommand);
1234 pid_t smime_invoke_sign (FILE **smimein, FILE **smimeout, FILE **smimeerr,
1235 int smimeinfd, int smimeoutfd, int smimeerrfd,
1238 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1239 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1240 SmimeCertToUse, SmimeIntermediateToUse,
1247 BODY *smime_build_smime_entity (BODY *a, char *certlist)
1249 char buf[LONG_STRING], certfile[LONG_STRING];
1250 char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1251 char smimeinfile[_POSIX_PATH_MAX];
1252 char *cert_start = certlist, *cert_end = certlist;
1253 FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1258 mutt_mktemp (tempfile);
1259 if ((fpout = safe_fopen (tempfile, "w+")) == NULL)
1261 mutt_perror (tempfile);
1265 mutt_mktemp (smimeerrfile);
1266 if ((smimeerr = safe_fopen (smimeerrfile, "w+")) == NULL)
1268 mutt_perror (smimeerrfile);
1270 mutt_unlink (tempfile);
1273 mutt_unlink (smimeerrfile);
1275 mutt_mktemp (smimeinfile);
1276 if ((fptmp = safe_fopen (smimeinfile, "w+")) == NULL)
1278 mutt_perror (smimeinfile);
1279 mutt_unlink (tempfile);
1288 int off = mutt_strlen (certfile);
1289 while (*++cert_end && *cert_end != '\n');
1290 if (!*cert_end) break;
1292 snprintf (certfile+off, sizeof (certfile)-off, " %s/%s",
1293 NONULL(SmimeCertificates), cert_start);
1295 cert_start = cert_end;
1299 /* write a MIME entity */
1300 mutt_write_mime_header (a, fptmp);
1301 fputc ('\n', fptmp);
1302 mutt_write_mime_body (a, fptmp);
1306 smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1307 fileno (fpout), fileno (smimeerr),
1308 smimeinfile, certfile)) == -1)
1311 mutt_unlink (smimeinfile);
1312 mutt_unlink (certfile);
1318 mutt_wait_filter (thepid);
1319 mutt_unlink (smimeinfile);
1320 mutt_unlink (certfile);
1324 empty = (fgetc (fpout) == EOF);
1329 while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL)
1332 fputs (buf, stdout);
1336 /* pause if there is any error output from SMIME */
1338 mutt_any_key_to_continue (NULL);
1342 /* fatal error while trying to encrypt message */
1343 if (!err) mutt_any_key_to_continue _("No output from OpenSSL..");
1344 mutt_unlink (tempfile);
1348 t = mutt_new_body ();
1349 t->type = TYPEAPPLICATION;
1350 t->subtype = safe_strdup ("x-pkcs7-mime");
1351 mutt_set_parameter ("name", "smime.p7m", &t->parameter);
1352 mutt_set_parameter ("smime-type", "enveloped-data", &t->parameter);
1353 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
1355 t->disposition = DISPATTACH;
1356 t->d_filename = safe_strdup ("smime.p7m");
1357 t->filename = safe_strdup (tempfile);
1358 t->unlink = 1; /*delete after sending the message */
1368 BODY *smime_sign_message (BODY *a )
1371 char buffer[LONG_STRING];
1372 char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1373 FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1377 char *intermediates = smime_get_field_from_db(NULL, SmimeDefaultKey, 1, 1);
1381 mutt_message(_("Warning: Intermediate certificate not found."));
1382 intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
1385 intermediates[mutt_strlen (intermediates)-1] = '\0';
1387 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
1389 mutt_mktemp (filetosign);
1390 if ((sfp = safe_fopen (filetosign, "w+")) == NULL)
1392 mutt_perror (filetosign);
1396 mutt_mktemp (signedfile);
1397 if ((smimeout = safe_fopen (signedfile, "w+")) == NULL)
1399 mutt_perror (signedfile);
1401 mutt_unlink (filetosign);
1405 mutt_write_mime_header (a, sfp);
1407 mutt_write_mime_body (a, sfp);
1412 snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1413 NONULL(SmimeKeys), SmimeDefaultKey);
1415 snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1416 NONULL(SmimeCertificates), SmimeDefaultKey);
1418 snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1419 NONULL(SmimeCertificates), intermediates);
1423 if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1424 -1, fileno (smimeout), -1, filetosign)) == -1)
1426 mutt_perror _("Can't open OpenSSL subprocess!");
1428 mutt_unlink (signedfile);
1429 mutt_unlink (filetosign);
1432 fputs (SmimePass, smimein);
1433 fputc ('\n', smimein);
1437 mutt_wait_filter (thepid);
1439 /* check for errors from OpenSSL */
1443 while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL)
1446 fputs (buffer, stdout);
1453 empty = (fgetc (smimeout) == EOF);
1456 mutt_unlink (filetosign);
1460 mutt_any_key_to_continue (NULL);
1464 mutt_any_key_to_continue _("No output from OpenSSL...");
1465 mutt_unlink (signedfile);
1466 return (NULL); /* fatal error while signing */
1469 t = mutt_new_body ();
1470 t->type = TYPEMULTIPART;
1471 t->subtype = safe_strdup ("signed");
1472 t->encoding = ENC7BIT;
1474 t->disposition = DISPINLINE;
1476 mutt_generate_boundary (&t->parameter);
1477 /* check if this can be extracted from private key somehow.... */
1478 mutt_set_parameter ("micalg", "sha1", &t->parameter);
1479 mutt_set_parameter ("protocol", "application/x-pkcs7-signature",
1485 t->parts->next = mutt_new_body ();
1487 t->type = TYPEAPPLICATION;
1488 t->subtype = safe_strdup ("x-pkcs7-signature");
1489 t->filename = safe_strdup (signedfile);
1490 t->d_filename = safe_strdup ("smime.p7s");
1492 t->disposition = DISPATTACH;
1493 t->encoding = ENCBASE64;
1494 t->unlink = 1; /* ok to remove this file after sending. */
1506 * Handling S/MIME - bodies.
1515 pid_t smime_invoke_verify (FILE **smimein, FILE **smimeout, FILE **smimeerr,
1516 int smimeinfd, int smimeoutfd, int smimeerrfd,
1517 const char *fname, const char *sig_fname, int opaque)
1519 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1520 smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1521 (opaque ? SmimeVerifyOpaqueCommand : SmimeVerifyCommand));
1526 pid_t smime_invoke_decrypt (FILE **smimein, FILE **smimeout, FILE **smimeerr,
1527 int smimeinfd, int smimeoutfd, int smimeerrfd,
1530 return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1531 smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1532 SmimeCertToUse, NULL, SmimeDecryptCommand);
1537 int smime_verify_one (BODY *sigbdy, STATE *s, const char *tempfile)
1539 char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1540 FILE *fp=NULL, *smimeout=NULL, *smimeerr=NULL;
1545 size_t tmplength = 0;
1546 int origType = sigbdy->type;
1547 char *savePrefix = NULL;
1550 snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1552 /* decode to a tempfile, saving the original destination */
1554 if ((s->fpout = safe_fopen (signedfile, "w")) == NULL)
1556 mutt_perror (signedfile);
1559 /* decoding the attachment changes the size and offset, so save a copy
1560 * of the "real" values now, and restore them after processing
1562 tmplength = sigbdy->length;
1563 tmpoffset = sigbdy->offset;
1565 /* if we are decoding binary bodies, we don't want to prefix each
1566 * line with the prefix or else the data will get corrupted.
1568 savePrefix = s->prefix;
1571 mutt_decode_attachment (sigbdy, s);
1573 sigbdy->length = ftell (s->fpout);
1577 /* restore final destination and substitute the tempfile for input */
1580 s->fpin = fopen (signedfile, "r");
1582 /* restore the prefix */
1583 s->prefix = savePrefix;
1585 sigbdy->type = origType;
1588 mutt_mktemp (smimeerrfile);
1589 if (!(smimeerr = safe_fopen (smimeerrfile, "w+")))
1591 mutt_perror (smimeerrfile);
1592 mutt_unlink (signedfile);
1596 crypt_current_time (s, "OpenSSL");
1598 if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1599 -1, -1, fileno (smimeerr),
1600 tempfile, signedfile, 0)) != -1)
1605 if (mutt_wait_filter (thepid))
1616 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1617 if (linelen && !mutt_strcasecmp (line, "verification successful"))
1626 mutt_copy_stream (smimeerr, s->fpout);
1629 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1631 mutt_unlink (signedfile);
1632 mutt_unlink (smimeerrfile);
1634 sigbdy->length = tmplength;
1635 sigbdy->offset = tmpoffset;
1637 /* restore the original source stream */
1650 This handles application/pkcs7-mime which can either be a signed
1651 or an encrypted message.
1654 static BODY *smime_handle_entity (BODY *m, STATE *s, FILE *outFile)
1659 char buf[HUGE_STRING];
1660 char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1661 char tmpfname[_POSIX_PATH_MAX];
1662 char tmptmpfname[_POSIX_PATH_MAX];
1663 FILE *smimeout = NULL, *smimein=NULL, *smimeerr=NULL;
1664 FILE *tmpfp=NULL, *tmpfp_buffer=NULL, *fpout=NULL;
1668 unsigned int type = mutt_is_application_smime (m);
1670 if (!(type & APPLICATION_SMIME)) return NULL;
1672 mutt_mktemp (outfile);
1673 if ((smimeout = safe_fopen (outfile, "w+")) == NULL)
1675 mutt_perror (outfile);
1679 mutt_mktemp (errfile);
1680 if ((smimeerr = safe_fopen (errfile, "w+")) == NULL)
1682 mutt_perror (errfile);
1683 fclose (smimeout); smimeout = NULL;
1686 mutt_unlink (errfile);
1689 mutt_mktemp (tmpfname);
1690 if ((tmpfp = safe_fopen (tmpfname, "w+")) == NULL)
1692 mutt_perror (tmpfname);
1693 fclose (smimeout); smimeout = NULL;
1694 fclose (smimeerr); smimeerr = NULL;
1698 fseek (s->fpin, m->offset, 0);
1699 last_pos = m->offset;
1701 mutt_copy_bytes (s->fpin, tmpfp, m->length);
1706 if ((type & ENCRYPT) &&
1707 (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1708 fileno (smimeout), fileno (smimeerr), tmpfname)) == -1)
1710 fclose (smimeout); smimeout = NULL;
1711 mutt_unlink (tmpfname);
1712 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1715 else if ((type & SIGNOPAQUE) &&
1716 (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1717 fileno (smimeout), fileno (smimeerr), NULL,
1718 tmpfname, SIGNOPAQUE)) == -1)
1720 fclose (smimeout); smimeout = NULL;
1721 mutt_unlink (tmpfname);
1722 state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1729 if (!smime_valid_passphrase ())
1730 smime_void_passphrase ();
1731 fputs (SmimePass, smimein);
1732 fputc ('\n', smimein);
1737 mutt_wait_filter (thepid);
1738 mutt_unlink (tmpfname);
1741 if (s->flags & M_DISPLAY)
1745 if ((c = fgetc (smimeerr)) != EOF)
1747 ungetc (c, smimeerr);
1749 crypt_current_time (s, "OpenSSL");
1750 mutt_copy_stream (smimeerr, s->fpout);
1751 state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1755 state_attach_puts (_("[-- The following data is S/MIME"
1756 " encrypted --]\n"), s);
1758 state_attach_puts (_("[-- The following data is S/MIME signed --]\n"), s);
1766 if (outFile) fpout = outFile;
1769 mutt_mktemp (tmptmpfname);
1770 if ((fpout = safe_fopen (tmptmpfname, "w+")) == NULL)
1772 mutt_perror(tmptmpfname);
1773 fclose (smimeout); smimeout = NULL;
1777 while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL)
1779 len = mutt_strlen (buf);
1780 if (len > 1 && buf[len - 2] == '\r')
1791 if ((p = mutt_read_mime_header (fpout, 0)) != NULL)
1793 fstat (fileno (fpout), &info);
1794 p->length = info.st_size - p->offset;
1796 mutt_parse_part (fpout, p);
1800 tmpfp_buffer = s->fpin;
1802 mutt_body_handler (p, s);
1803 s->fpin = tmpfp_buffer;
1809 mutt_unlink (outfile);
1814 mutt_unlink (tmptmpfname);
1819 if (s->flags & M_DISPLAY)
1822 state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1824 state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1827 if (type & SIGNOPAQUE)
1835 line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1836 if (linelen && !mutt_strcasecmp (line, "verification successful"))
1842 m->goodsig = p->goodsig;
1843 m->badsig = p->badsig;
1854 int smime_decrypt_mime (FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
1858 char tempfile[_POSIX_PATH_MAX];
1860 long tmpoffset = b->offset;
1861 size_t tmplength = b->length;
1862 int origType = b->type;
1865 if (!mutt_is_application_smime (b))
1871 memset (&s, 0, sizeof (s));
1873 fseek (s.fpin, b->offset, 0);
1875 mutt_mktemp (tempfile);
1876 if ((tmpfp = safe_fopen (tempfile, "w+")) == NULL)
1878 mutt_perror (tempfile);
1882 mutt_unlink (tempfile);
1884 mutt_decode_attachment (b, &s);
1886 b->length = ftell (s.fpout);
1892 mutt_mktemp (tempfile);
1893 if ((*fpout = safe_fopen (tempfile, "w+")) == NULL)
1895 mutt_perror (tempfile);
1898 mutt_unlink (tempfile);
1900 *cur = smime_handle_entity (b, &s, *fpout);
1901 (*cur)->goodsig = b->goodsig;
1902 (*cur)->badsig = b->badsig;
1904 b->length = tmplength;
1905 b->offset = tmpoffset;
1914 void smime_application_smime_handler (BODY *m, STATE *s)
1917 smime_handle_entity (m, s, NULL);
1920 #endif /* CRYPT_BACKEND_CLASSIC_SMIME */