33c53b001db6190a7de2c00b6894e46035fc047f
[apps/madmutt.git] / lib-crypt / smime.c
1 /*
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
6  *
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.
10  */
11
12 #include <lib-lib/lib-lib.h>
13
14 #ifdef HAVE_SYS_RESOURCE_H
15 #  include <sys/resource.h>
16 #endif
17
18 #include <lib-mime/mime.h>
19 #include <lib-sys/unix.h>
20
21 #include <lib-ui/curses.h>
22 #include <lib-ui/enter.h>
23 #include <lib-ui/menu.h>
24
25 #include "alias.h"
26 #include "handler.h"
27 #include "copy.h"
28 #include "alias.h"
29
30 #include "crypt.h"
31 #include "smime.h"
32
33 struct smime_command_context {
34   const char *key;              /* %k */
35   const char *cryptalg;         /* %a */
36   const char *fname;            /* %f */
37   const char *sig_fname;        /* %s */
38   const char *certificates;     /* %c */
39   const char *intermediates;    /* %i */
40 };
41
42
43 typedef struct {
44   unsigned int hash;
45   char suffix;
46   char email[256];
47   char nick[256];
48   char trust;                   /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
49   short public;                 /* 1=public 0=private */
50 } smime_id;
51
52
53 char SmimePass[STRING];
54 time_t SmimeExptime = 0;        /* when does the cached passphrase expire? */
55
56
57 static char SmimeKeyToUse[_POSIX_PATH_MAX] = { 0 };
58 static char SmimeCertToUse[_POSIX_PATH_MAX];
59 static char SmimeIntermediateToUse[_POSIX_PATH_MAX];
60
61
62 /*
63  * Create a format string to be used with scanf.
64  * To use it, write, for instance, MUTT_FORMAT(HUGE_STRING).
65  * 
66  * See K&R 2nd ed, p. 231 for an explanation.
67  */
68 #define _MUTT_FORMAT_2(a,b)     "%" a  b
69 #define _MUTT_FORMAT_1(a, b)    _MUTT_FORMAT_2(#a, b)
70 #define MUTT_FORMAT(a)          _MUTT_FORMAT_1(a, "s")
71
72
73 /*
74  *     Queries and passphrase handling.
75  */
76
77
78 /* these are copies from pgp.c */
79
80
81 void smime_void_passphrase (void)
82 {
83   p_clear(SmimePass, countof(SmimePass));
84   SmimeExptime = 0;
85 }
86
87 int smime_valid_passphrase (void)
88 {
89   time_t now = time (NULL);
90
91   if (now < SmimeExptime)
92     /* Use cached copy.  */
93     return 1;
94
95   smime_void_passphrase ();
96
97   if (mutt_get_field_unbuffered (_("Enter S/MIME passphrase:"), SmimePass,
98                                  sizeof (SmimePass), M_PASS) == 0) {
99     SmimeExptime = time (NULL) + SmimeTimeout;
100     return (1);
101   }
102   else
103     SmimeExptime = 0;
104
105   return 0;
106 }
107
108
109 /*
110  *     The OpenSSL interface
111  */
112
113 /* This is almost identical to ppgp's invoking interface. */
114
115 static const char *
116 _mutt_fmt_smime_command (char *dest, ssize_t destlen, char op,
117                          const char *src, const char *prefix,
118                          const char *ifstr, const char *elstr,
119                          anytype data, format_flag flags)
120 {
121   char fmt[16];
122   struct smime_command_context *cctx = data.ptr;
123   int optional = (flags & M_FORMAT_OPTIONAL);
124
125   switch (op) {
126   case 'C':
127     {
128       if (!optional) {
129         char path[_POSIX_PATH_MAX];
130         char buf1[LONG_STRING], buf2[LONG_STRING];
131         struct stat sb;
132
133         m_strcpy(path, sizeof(path), NONULL(SmimeCALocation));
134         mutt_expand_path (path, sizeof (path));
135         mutt_quote_filename (buf1, sizeof (buf1), path);
136
137         if (stat (path, &sb) != 0 || !S_ISDIR (sb.st_mode))
138           snprintf (buf2, sizeof (buf2), "-CAfile %s", buf1);
139         else
140           snprintf (buf2, sizeof (buf2), "-CApath %s", buf1);
141
142         snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
143         snprintf (dest, destlen, fmt, buf2);
144       }
145       else if (!SmimeCALocation)
146         optional = 0;
147       break;
148     }
149
150   case 'c':
151     {                           /* certificate (list) */
152       if (!optional) {
153         snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
154         snprintf (dest, destlen, fmt, NONULL (cctx->certificates));
155       }
156       else if (!cctx->certificates)
157         optional = 0;
158       break;
159     }
160
161   case 'i':
162     {                           /* intermediate certificates  */
163       if (!optional) {
164         snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
165         snprintf (dest, destlen, fmt, NONULL (cctx->intermediates));
166       }
167       else if (!cctx->intermediates)
168         optional = 0;
169       break;
170     }
171
172   case 's':
173     {                           /* detached signature */
174       if (!optional) {
175         snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
176         snprintf (dest, destlen, fmt, NONULL (cctx->sig_fname));
177       }
178       else if (!cctx->sig_fname)
179         optional = 0;
180       break;
181     }
182
183   case 'k':
184     {                           /* private key */
185       if (!optional) {
186         snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
187         snprintf (dest, destlen, fmt, NONULL (cctx->key));
188       }
189       else if (!cctx->key)
190         optional = 0;
191       break;
192     }
193
194   case 'a':
195     {                           /* algorithm for encryption */
196       if (!optional) {
197         snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
198         snprintf (dest, destlen, fmt, NONULL (cctx->cryptalg));
199       }
200       else if (!cctx->key)
201         optional = 0;
202       break;
203     }
204
205   case 'f':
206     {                           /* file to process */
207       if (!optional) {
208         snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
209         snprintf (dest, destlen, fmt, NONULL (cctx->fname));
210       }
211       else if (!cctx->fname)
212         optional = 0;
213       break;
214     }
215
216   default:
217     *dest = '\0';
218     break;
219   }
220
221   if (flags & M_FORMAT_OPTIONAL)
222     m_strformat(dest, destlen, optional ? ifstr : elstr,
223                 _mutt_fmt_smime_command, data, 0);
224
225   return src;
226 }
227
228
229
230 static void mutt_smime_command (char *d, ssize_t dlen,
231                                 struct smime_command_context *cctx,
232                                 const char *fmt)
233 {
234     m_strformat(d, dlen, fmt, _mutt_fmt_smime_command, cctx, 0);
235 }
236
237 static pid_t smime_invoke (FILE ** smimein, FILE ** smimeout,
238                            FILE ** smimeerr, int smimeinfd, int smimeoutfd,
239                            int smimeerrfd, const char *fname,
240                            const char *sig_fname, const char *cryptalg,
241                            const char *key, const char *certificates,
242                            const char *intermediates, const char *format)
243 {
244   struct smime_command_context cctx;
245   char cmd[HUGE_STRING];
246
247   p_clear(&cctx, 1);
248
249   if (!format || !*format)
250     return (pid_t) - 1;
251
252   cctx.fname = fname;
253   cctx.sig_fname = sig_fname;
254   cctx.key = key;
255   cctx.cryptalg = cryptalg;
256   cctx.certificates = certificates;
257   cctx.intermediates = intermediates;
258
259   mutt_smime_command (cmd, sizeof (cmd), &cctx, format);
260
261   return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr,
262                                 smimeinfd, smimeoutfd, smimeerrfd);
263 }
264
265
266
267
268
269
270 /*
271  *    Key and certificate handling.
272  */
273
274
275
276 /* 
277    Search the certificate index for given mailbox.
278    return certificate file name.
279 */
280
281 static void smime_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
282 {
283   smime_id *Table = (smime_id *) menu->data;
284   smime_id this = Table[num];
285   const char *truststate;
286
287   switch (this.trust) {
288   case 't':
289     truststate = N_("Trusted   ");
290     break;
291   case 'v':
292     truststate = N_("Verified  ");
293     break;
294   case 'u':
295     truststate = N_("Unverified");
296     break;
297   case 'e':
298     truststate = N_("Expired   ");
299     break;
300   case 'r':
301     truststate = N_("Revoked   ");
302     break;
303   case 'i':
304     truststate = N_("Invalid   ");
305     break;
306   default:
307     truststate = N_("Unknown   ");
308   }
309   if (this.public)
310     snprintf (s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix,
311               truststate, this.email, this.nick);
312   else
313     snprintf (s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix,
314               this.email, this.nick);
315 }
316
317
318
319
320
321 char *smime_ask_for_key (char *prompt, char *mailbox __attribute__((unused)),
322                          short public)
323 {
324   char *fname;
325   smime_id *Table;
326   long cert_num;                /* Will contain the number of certificates.
327                                  * To be able to get it, the .index file will be read twice... */
328   char index_file[_POSIX_PATH_MAX];
329   FILE *idx;
330   char buf[LONG_STRING];
331   char fields[5][STRING];
332   int numFields, hash_suffix, done, cur;        /* The current entry */
333   MUTTMENU *menu;
334   unsigned int hash;
335   char helpstr[HUGE_STRING * 3];
336   char qry[256];
337   char title[256];
338
339   if (!prompt)
340     prompt = _("Enter keyID: ");
341   snprintf (index_file, sizeof (index_file), "%s/.index",
342             public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys));
343
344   idx = fopen (index_file, "r");
345   if (idx == NULL) {
346     mutt_perror (index_file);
347     return NULL;
348   }
349   /* Count Lines */
350   cert_num = 0;
351   while (!feof (idx)) {
352     if (fgets (buf, sizeof (buf), idx))
353       cert_num++;
354   }
355   m_fclose(&idx);
356
357   for (;;) {
358     *qry = 0;
359     if (mutt_get_field (prompt, qry, sizeof (qry), 0))
360       return NULL;
361     snprintf (title, sizeof (title),
362               _("S/MIME certificates matching \"%s\"."), qry);
363
364
365     idx = fopen (index_file, "r");
366     if (idx == NULL) {
367       mutt_perror (index_file);
368       return NULL;
369     }
370     /* Read Entries */
371     cur = 0;
372     Table = p_new(smime_id, cert_num);
373     while (!feof (idx)) {
374       numFields =
375         fscanf (idx, MUTT_FORMAT (STRING) " %x.%i " MUTT_FORMAT (STRING),
376                 fields[0], &hash, &hash_suffix, fields[2]);
377       if (public)
378         fscanf (idx, MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) "\n",
379                 fields[3], fields[4]);
380
381       /* 0=email 1=name 2=nick 3=intermediate 4=trust */
382       if (numFields < 2)
383         continue;
384
385       /* Check if query matches this certificate */
386       if (!m_stristr(fields[0], qry) && !m_stristr(fields[2], qry))
387         continue;
388
389       Table[cur].hash = hash;
390       Table[cur].suffix = hash_suffix;
391       m_strcpy(Table[cur].email, sizeof(Table[cur].email), fields[0]);
392       m_strcpy(Table[cur].nick,  sizeof(Table[cur].nick),  fields[2]);
393       Table[cur].trust = *fields[4];
394       Table[cur].public = public;
395
396       cur++;
397     }
398     m_fclose(&idx);
399
400     /* Make Helpstring */
401     helpstr[0] = 0;
402     mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_SMIME, OP_EXIT);
403     m_strcat(helpstr, sizeof(helpstr), buf);
404     mutt_make_help (buf, sizeof (buf), _("Select  "), MENU_SMIME,
405                     OP_GENERIC_SELECT_ENTRY);
406     m_strcat(helpstr, sizeof(helpstr), buf);
407     mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
408     m_strcat(helpstr, sizeof(helpstr), buf);
409
410     /* Create the menu */
411     menu = mutt_new_menu ();
412     menu->max = cur;
413     menu->make_entry = smime_entry;
414     menu->menu = MENU_SMIME;
415     menu->help = helpstr;
416     menu->data = Table;
417     menu->title = title;
418     /* sorting keys might be done later - TODO */
419
420     mutt_clear_error ();
421
422     done = 0;
423     hash = 0;
424     while (!done) {
425       switch (mutt_menuLoop (menu)) {
426       case OP_GENERIC_SELECT_ENTRY:
427         cur = menu->current;
428         hash = 1;
429         done = 1;
430         break;
431       case OP_EXIT:
432         hash = 0;
433         done = 1;
434         break;
435       }
436     }
437     if (hash) {
438       fname = p_new(char, 13); /* Hash + '.' + Suffix + \0 */
439       sprintf (fname, "%.8x.%i", Table[cur].hash, Table[cur].suffix);
440     }
441     else
442       fname = NULL;
443
444     mutt_menuDestroy (&menu);
445     p_delete(&Table);
446     set_option (OPTNEEDREDRAW);
447
448     if (fname)
449       return fname;
450   }
451 }
452
453
454
455 char *smime_get_field_from_db (char *mailbox, char *query, short public,
456                                short may_ask)
457 {
458   int addr_len, query_len, found = 0, ask = 0, choice = 0;
459   char cert_path[_POSIX_PATH_MAX];
460   char buf[LONG_STRING], prompt[STRING];
461   char fields[5][STRING];
462   char key[STRING];
463   int numFields;
464   struct stat info;
465   char key_trust_level = 0;
466   FILE *fp;
467
468   if (!mailbox && !query)
469     return (NULL);
470
471   addr_len = mailbox ? m_strlen(mailbox) : 0;
472   query_len = query ? m_strlen(query) : 0;
473
474   *key = '\0';
475
476   /* index-file format:
477      mailbox certfile label issuer_certfile trust_flags\n
478
479      certfile is a hash value generated by openssl.
480      Note that this was done according to the OpenSSL
481      specs on their CA-directory.
482
483    */
484   snprintf (cert_path, sizeof (cert_path), "%s/.index",
485             (public ? NONULL (SmimeCertificates) : NONULL (SmimeKeys)));
486
487   if (!stat (cert_path, &info)) {
488     if ((fp = safe_fopen (cert_path, "r")) == NULL) {
489       mutt_perror (cert_path);
490       return (NULL);
491     }
492
493     while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
494       if (mailbox && !(m_strncasecmp(mailbox, buf, addr_len))) {
495         numFields = sscanf (buf,
496                             MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
497                             MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
498                             MUTT_FORMAT (STRING) "\n",
499                             fields[0], fields[1],
500                             fields[2], fields[3], fields[4]);
501         if (numFields < 2)
502           continue;
503         if (mailbox && public &&
504             (!fields[4] ||
505              *fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
506           continue;
507
508         if (found) {
509           if (public && *fields[4] == 'u')
510             snprintf (prompt, sizeof (prompt),
511                       _
512                       ("ID %s is unverified. Do you want to use it for %s ?"),
513                       fields[1], mailbox);
514           else if (public && *fields[4] == 'v')
515             snprintf (prompt, sizeof (prompt),
516                       _("Use (untrusted!) ID %s for %s ?"),
517                       fields[1], mailbox);
518           else
519             snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
520                       fields[1], mailbox);
521           if (may_ask == 0)
522             choice = M_YES;
523           if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1) {
524             found = 0;
525             ask = 0;
526             *key = '\0';
527             break;
528           }
529           else if (choice == M_NO) {
530             ask = 1;
531             continue;
532           }
533           else if (choice == M_YES) {
534             m_strcpy(key, sizeof(key), fields[1]);
535             ask = 0;
536             break;
537           }
538         }
539         else {
540           if (public)
541             key_trust_level = *fields[4];
542           m_strcpy(key, sizeof(key), fields[1]);
543         }
544         found = 1;
545       }
546       else if (query) {
547         numFields = sscanf (buf,
548                             MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
549                             MUTT_FORMAT (STRING) " " MUTT_FORMAT (STRING) " "
550                             MUTT_FORMAT (STRING) "\n",
551                             fields[0], fields[1],
552                             fields[2], fields[3], fields[4]);
553
554         /* query = label: return certificate. */
555         if (numFields >= 3 &&
556             !(m_strncasecmp(query, fields[2], query_len))) {
557           ask = 0;
558           m_strcpy(key, sizeof(key), fields[1]);
559         }
560         /* query = certificate: return intermediate certificate. */
561         else if (numFields >= 4 &&
562                  !(m_strncasecmp(query, fields[1], query_len))) {
563           ask = 0;
564           m_strcpy(key, sizeof(key), fields[3]);
565         }
566       }
567
568     m_fclose(&fp);
569
570     if (ask) {
571       if (public && *fields[4] == 'u')
572         snprintf (prompt, sizeof (prompt),
573                   _("ID %s is unverified. Do you want to use it for %s ?"),
574                   fields[1], mailbox);
575       else if (public && *fields[4] == 'v')
576         snprintf (prompt, sizeof (prompt),
577                   _("Use (untrusted!) ID %s for %s ?"), fields[1], mailbox);
578       else
579         snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"), key,
580                   mailbox);
581       choice = mutt_yesorno (prompt, M_NO);
582       if (choice == -1 || choice == M_NO)
583         *key = '\0';
584     }
585     else if (key_trust_level && may_ask) {
586       if (key_trust_level == 'u') {
587         snprintf (prompt, sizeof (prompt),
588                   _("ID %s is unverified. Do you want to use it for %s ?"),
589                   key, mailbox);
590         choice = mutt_yesorno (prompt, M_NO);
591         if (choice != M_YES)
592           *key = '\0';
593       }
594       else if (key_trust_level == 'v') {
595         mutt_error (_
596                     ("Warning: You have not yet decided to trust ID %s. (any key to continue)"),
597                     key);
598         mutt_sleep (5);
599       }
600     }
601
602   }
603
604   /* Note: m_strdup("") returns NULL. */
605   return m_strdup(key);
606 }
607
608 /* 
609    This sets the '*ToUse' variables for an upcoming decryption, where
610    the reuquired key is different from SmimeDefaultKey.
611 */
612 static void _smime_getkeys (char *mailbox)
613 {
614   char *k = NULL;
615   char buf[STRING];
616
617   k = smime_get_field_from_db (mailbox, NULL, 0, 1);
618
619   if (!k) {
620     snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), mailbox);
621     k = smime_ask_for_key (buf, mailbox, 0);
622   }
623
624   if (k) {
625     /* the key used last time. */
626     if (*SmimeKeyToUse &&
627         !m_strcasecmp(k, SmimeKeyToUse + m_strlen(SmimeKeys) + 1)) {
628       p_delete(&k);
629       return;
630     }
631     else
632       smime_void_passphrase ();
633
634     snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
635               NONULL (SmimeKeys), k);
636
637     snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
638               NONULL (SmimeCertificates), k);
639
640     if (m_strcasecmp(k, SmimeDefaultKey))
641       smime_void_passphrase ();
642
643     p_delete(&k);
644     return;
645   }
646
647   if (*SmimeKeyToUse) {
648     if (!m_strcasecmp(SmimeDefaultKey,
649                           SmimeKeyToUse + m_strlen(SmimeKeys) + 1))
650       return;
651
652     smime_void_passphrase ();
653   }
654
655   snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
656             NONULL (SmimeKeys), NONULL (SmimeDefaultKey));
657
658   snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
659             NONULL (SmimeCertificates), NONULL (SmimeDefaultKey));
660 }
661
662 void smime_getkeys (ENVELOPE * env)
663 {
664   address_t *t;
665   int found = 0;
666
667   if (option (OPTSDEFAULTDECRYPTKEY) && SmimeDefaultKey && *SmimeDefaultKey) {
668     snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
669               NONULL (SmimeKeys), SmimeDefaultKey);
670
671     snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
672               NONULL (SmimeCertificates), SmimeDefaultKey);
673
674     return;
675   }
676
677   for (t = env->to; !found && t; t = t->next)
678     if (mutt_addr_is_user (t)) {
679       found = 1;
680       _smime_getkeys (t->mailbox);
681     }
682   for (t = env->cc; !found && t; t = t->next)
683     if (mutt_addr_is_user (t)) {
684       found = 1;
685       _smime_getkeys (t->mailbox);
686     }
687   if (!found && (t = mutt_default_from ())) {
688     _smime_getkeys (t->mailbox);
689     address_list_wipe(&t);
690   }
691 }
692
693 /* This routine attempts to find the keyids of the recipients of a message.
694  * It returns NULL if any of the keys can not be found.
695  */
696
697 char *smime_findKeys (address_t * to, address_t * cc, address_t * bcc)
698 {
699   char *keyID, *keylist = NULL;
700   ssize_t keylist_size = 0;
701   ssize_t keylist_used = 0;
702   address_t *tmp = NULL, *addr = NULL;
703   address_t **last = &tmp;
704   address_t *p, *q;
705   int i;
706
707   for (i = 0; i < 3; i++) {
708     switch (i) {
709     case 0:
710       p = to;
711       break;
712     case 1:
713       p = cc;
714       break;
715     case 2:
716       p = bcc;
717       break;
718     default:
719       abort ();
720     }
721
722     *last = address_list_dup (p);
723     while (*last)
724       last = &((*last)->next);
725   }
726
727   rfc822_qualify(tmp, mutt_fqdn(1));
728
729   address_list_uniq(tmp);
730
731   for (p = tmp; p; p = p->next) {
732     char buf[LONG_STRING];
733
734     q = p;
735
736     if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
737       snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
738       keyID = smime_ask_for_key (buf, q->mailbox, 1);
739     }
740     if (!keyID) {
741       mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
742       p_delete(&keylist);
743       address_list_wipe(&tmp);
744       address_list_wipe(&addr);
745       return NULL;
746     }
747
748     keylist_size += m_strlen(keyID) + 2;
749     p_realloc(&keylist, keylist_size);
750     sprintf (keylist + keylist_used, "%s\n", keyID);
751     keylist_used = m_strlen(keylist);
752
753     address_list_wipe(&addr);
754
755   }
756   address_list_wipe(&tmp);
757   return (keylist);
758 }
759
760
761
762
763
764
765 static int smime_handle_cert_email (char *certificate, char *mailbox,
766                                     int copy, char ***buffer, int *num)
767 {
768   FILE *fpout = NULL, *fperr = NULL;
769   char tmpfname[_POSIX_PATH_MAX];
770   char email[STRING];
771   int ret = -1, count = 0;
772   pid_t thepid;
773
774   fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
775   if (!fperr) {
776     mutt_perror (tmpfname);
777     return 1;
778   }
779   mutt_unlink (tmpfname);
780
781   fpout = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
782   if (!fpout) {
783     m_fclose(&fperr);
784     mutt_perror (tmpfname);
785     return 1;
786   }
787   mutt_unlink (tmpfname);
788
789   if ((thepid = smime_invoke (NULL, NULL, NULL,
790                               -1, fileno (fpout), fileno (fperr),
791                               certificate, NULL, NULL, NULL, NULL, NULL,
792                               SmimeGetCertEmailCommand)) == -1) {
793     mutt_message (_("Error: unable to create OpenSSL subprocess!"));
794     m_fclose(&fperr);
795     m_fclose(&fpout);
796     return 1;
797   }
798
799   mutt_wait_filter (thepid);
800
801   fflush (fpout);
802   rewind (fpout);
803   rewind (fperr);
804   fflush (fperr);
805
806
807   while ((fgets (email, sizeof (email), fpout))) {
808     *(email + m_strlen(email) - 1) = '\0';
809     if (m_strncasecmp(email, mailbox, m_strlen(mailbox)) == 0)
810       ret = 1;
811
812     ret = ret < 0 ? 0 : ret;
813     count++;
814   }
815
816   if (ret == -1) {
817     mutt_endwin (NULL);
818     mutt_copy_stream (fperr, stdout);
819     mutt_any_key_to_continue (_
820                               ("Error: unable to create OpenSSL subprocess!"));
821     ret = 1;
822   }
823   else if (!ret)
824     ret = 1;
825   else
826     ret = 0;
827
828   if (copy && buffer && num) {
829     (*num) = count;
830     *buffer = p_new(char *, count);
831     count = 0;
832
833     rewind (fpout);
834     while ((fgets (email, sizeof (email), fpout))) {
835       *(email + m_strlen(email) - 1) = '\0';
836       (*buffer)[count] = p_dupstr(email, m_strlen(email));
837       count++;
838     }
839   }
840   else if (copy)
841     ret = 2;
842
843   m_fclose(&fpout);
844   m_fclose(&fperr);
845
846   return ret;
847 }
848
849
850
851 static char *smime_extract_certificate (char *infile)
852 {
853   FILE *fpout = NULL, *fperr = NULL;
854   char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
855   char tmpfname[_POSIX_PATH_MAX];
856   pid_t thepid;
857   int empty;
858
859
860   fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
861   if (!fperr) {
862     mutt_perror (tmpfname);
863     return NULL;
864   }
865   mutt_unlink (tmpfname);
866
867   fpout = m_tempfile (pk7out, sizeof(tmpfname), NONULL(Tempdir), NULL);
868   if (!fpout) {
869     m_fclose(&fperr);
870     mutt_perror (pk7out);
871     return NULL;
872   }
873
874   /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
875      extract the full set of certificates directly.
876    */
877   if ((thepid = smime_invoke (NULL, NULL, NULL,
878                               -1, fileno (fpout), fileno (fperr),
879                               infile, NULL, NULL, NULL, NULL, NULL,
880                               SmimePk7outCommand)) == -1) {
881     mutt_any_key_to_continue (_
882                               ("Error: unable to create OpenSSL subprocess!"));
883     m_fclose(&fperr);
884     m_fclose(&fpout);
885     mutt_unlink (pk7out);
886     return NULL;
887   }
888
889   mutt_wait_filter (thepid);
890
891
892   fflush (fpout);
893   rewind (fpout);
894   rewind (fperr);
895   fflush (fperr);
896
897   empty = (fgetc (fpout) == EOF);
898
899   m_fclose(&fpout);
900
901   if (empty) {
902     mutt_perror (pk7out);
903     mutt_copy_stream (fperr, stdout);
904     m_fclose(&fperr);
905     mutt_unlink (pk7out);
906     return NULL;
907   }
908
909   fpout = m_tempfile (certfile, sizeof(certfile), NONULL(Tempdir), NULL);
910   if (!fpout) {
911     m_fclose(&fperr);
912     mutt_unlink (pk7out);
913     mutt_perror (certfile);
914     return NULL;
915   }
916
917   /* Step 2: Extract the certificates from a PKCS#7 structure.
918    */
919   if ((thepid = smime_invoke (NULL, NULL, NULL,
920                               -1, fileno (fpout), fileno (fperr),
921                               pk7out, NULL, NULL, NULL, NULL, NULL,
922                               SmimeGetCertCommand)) == -1) {
923     mutt_any_key_to_continue (_
924                               ("Error: unable to create OpenSSL subprocess!"));
925     m_fclose(&fperr);
926     m_fclose(&fpout);
927     mutt_unlink (pk7out);
928     mutt_unlink (certfile);
929     return NULL;
930   }
931
932   mutt_wait_filter (thepid);
933
934   mutt_unlink (pk7out);
935
936   fflush (fpout);
937   rewind (fpout);
938   rewind (fperr);
939   fflush (fperr);
940   empty = (fgetc (fpout) == EOF);
941   if (empty) {
942     mutt_copy_stream (fperr, stdout);
943     m_fclose(&fpout);
944     m_fclose(&fperr);
945     mutt_unlink (certfile);
946     return NULL;
947   }
948
949   m_fclose(&fpout);
950   m_fclose(&fperr);
951
952   return m_strdup(certfile);
953 }
954
955 static char *smime_extract_signer_certificate (char *infile)
956 {
957   FILE *fpout = NULL, *fperr = NULL;
958   char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
959   char tmpfname[_POSIX_PATH_MAX];
960   pid_t thepid;
961   int empty;
962
963   fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
964   if (!fperr) {
965     mutt_perror (tmpfname);
966     return NULL;
967   }
968   mutt_unlink (tmpfname);
969
970   m_tempfile (certfile, sizeof(certfile), NONULL(Tempdir), NULL);
971   if (!fpout) {
972     m_fclose(&fperr);
973     mutt_perror (certfile);
974     return NULL;
975   }
976
977   /* Extract signer's certificate
978    */
979   if ((thepid = smime_invoke (NULL, NULL, NULL,
980                               -1, -1, fileno (fperr),
981                               infile, NULL, NULL, NULL, certfile, NULL,
982                               SmimeGetSignerCertCommand)) == -1) {
983     mutt_any_key_to_continue (_
984                               ("Error: unable to create OpenSSL subprocess!"));
985     m_fclose(&fperr);
986     m_fclose(&fpout);
987     mutt_unlink (pk7out);
988     mutt_unlink (certfile);
989     return NULL;
990   }
991
992   mutt_wait_filter (thepid);
993
994   fflush (fpout);
995   rewind (fpout);
996   rewind (fperr);
997   fflush (fperr);
998   empty = (fgetc (fpout) == EOF);
999   m_fclose(&fpout);
1000
1001   if (empty) {
1002     mutt_endwin (NULL);
1003     mutt_copy_stream (fperr, stdout);
1004     mutt_any_key_to_continue (NULL);
1005     m_fclose(&fperr);
1006     mutt_unlink (certfile);
1007     return NULL;
1008   }
1009
1010   m_fclose(&fperr);
1011
1012   return m_strdup(certfile);
1013 }
1014
1015 /* Add a certificate and update index file (externally). */
1016
1017 void smime_invoke_import (char *infile, char *mailbox __attribute__ ((unused)))
1018 {
1019   char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1020   FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1021   pid_t thepid = -1;
1022
1023  fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1024   if (!fperr) {
1025     mutt_perror (tmpfname);
1026     return;
1027   }
1028   mutt_unlink (tmpfname);
1029
1030   fpout = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1031   if (!fpout) {
1032     m_fclose(&fperr);
1033     mutt_perror (tmpfname);
1034     return;
1035   }
1036   mutt_unlink (tmpfname);
1037
1038   buf[0] = '\0';
1039   if (option (OPTASKCERTLABEL))
1040     mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1041
1042   mutt_endwin (NULL);
1043   if ((certfile = smime_extract_certificate (infile))) {
1044     mutt_endwin (NULL);
1045
1046     if ((thepid = smime_invoke (&smimein, NULL, NULL,
1047                                 -1, fileno (fpout), fileno (fperr),
1048                                 certfile, NULL, NULL, NULL, NULL, NULL,
1049                                 SmimeImportCertCommand)) == -1) {
1050       mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1051       return;
1052     }
1053     fputs (buf, smimein);
1054     fputc ('\n', smimein);
1055     m_fclose(&smimein);
1056
1057     mutt_wait_filter (thepid);
1058
1059     mutt_unlink (certfile);
1060     p_delete(&certfile);
1061   }
1062
1063   fflush (fpout);
1064   rewind (fpout);
1065   fflush (fperr);
1066   rewind (fperr);
1067
1068   mutt_copy_stream (fpout, stdout);
1069   mutt_copy_stream (fperr, stdout);
1070
1071   m_fclose(&fpout);
1072   m_fclose(&fperr);
1073 }
1074
1075 int smime_verify_sender (HEADER * h)
1076 {
1077   char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1078   FILE *fpout;
1079   int retval = 1;
1080
1081   fpout = m_tempfile (tempfname, sizeof(tempfname), NONULL(Tempdir), NULL);
1082   if (!fpout) {
1083     mutt_perror (_("Can't create temporary file"));
1084     return 1;
1085   }
1086
1087   if (h->security & ENCRYPT)
1088     mutt_copy_message (fpout, Context, h,
1089                        M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1090                        CH_MIME | CH_WEED | CH_NONEWLINE);
1091   else
1092     mutt_copy_message (fpout, Context, h, 0, 0);
1093
1094   fflush (fpout);
1095   m_fclose(&fpout);
1096
1097   if (h->env->from) {
1098     h->env->from = mutt_expand_aliases (h->env->from);
1099     mbox = h->env->from->mailbox;
1100   }
1101   else if (h->env->sender) {
1102     h->env->sender = mutt_expand_aliases (h->env->sender);
1103     mbox = h->env->sender->mailbox;
1104   }
1105
1106   if (mbox) {
1107     if ((certfile = smime_extract_signer_certificate (tempfname))) {
1108       mutt_unlink (tempfname);
1109       if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1110         if (isendwin ())
1111           mutt_any_key_to_continue (NULL);
1112       }
1113       else
1114         retval = 0;
1115       mutt_unlink (certfile);
1116       p_delete(&certfile);
1117     }
1118     else
1119       mutt_any_key_to_continue (_("no certfile"));
1120   }
1121   else
1122     mutt_any_key_to_continue (_("no mbox"));
1123
1124   mutt_unlink (tempfname);
1125   return retval;
1126 }
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136 /*
1137  *    Creating S/MIME - bodies.
1138  */
1139
1140
1141
1142
1143 static
1144 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1145                             FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1146                             int smimeerrfd, const char *fname,
1147                             const char *uids)
1148 {
1149   return smime_invoke (smimein, smimeout, smimeerr,
1150                        smimeinfd, smimeoutfd, smimeerrfd,
1151                        fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1152                        SmimeEncryptCommand);
1153 }
1154
1155
1156 static
1157 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1158                          int smimeinfd, int smimeoutfd, int smimeerrfd,
1159                          const char *fname)
1160 {
1161   return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1162                        smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1163                        SmimeCertToUse, SmimeIntermediateToUse,
1164                        SmimeSignCommand);
1165 }
1166
1167
1168
1169
1170 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1171 {
1172   char buf[LONG_STRING], certfile[LONG_STRING];
1173   char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1174   char smimeinfile[_POSIX_PATH_MAX];
1175   char *cert_start = certlist, *cert_end = certlist;
1176   FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1177   BODY *t;
1178   int err = 0, empty;
1179   pid_t thepid;
1180
1181   fpout = m_tempfile (tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
1182   if (!fpout) {
1183     mutt_perror (_("Can't create temporary file"));
1184     return NULL;
1185   }
1186
1187   smimeerr = m_tempfile (smimeerrfile, sizeof(smimeerrfile), NONULL(Tempdir), NULL);
1188   if (!smimeerr) {
1189     mutt_perror (smimeerrfile);
1190     m_fclose(&fpout);
1191     mutt_unlink (tempfile);
1192     return NULL;
1193   }
1194   mutt_unlink (smimeerrfile);
1195
1196   fptmp = m_tempfile (smimeinfile, sizeof(smimeinfile), NONULL(Tempdir), NULL);
1197   if (!fptmp) {
1198     mutt_perror (smimeinfile);
1199     mutt_unlink (tempfile);
1200     m_fclose(&fpout);
1201     m_fclose(&smimeerr);
1202     return NULL;
1203   }
1204
1205   *certfile = '\0';
1206   while (1) {
1207     int off = m_strlen(certfile);
1208
1209     while (*++cert_end && *cert_end != '\n');
1210     if (!*cert_end)
1211       break;
1212     *cert_end = '\0';
1213     snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1214               NONULL (SmimeCertificates), cert_start);
1215     *cert_end = '\n';
1216     cert_start = cert_end;
1217     cert_start++;
1218   }
1219
1220   /* write a MIME entity */
1221   mutt_write_mime_header (a, fptmp);
1222   fputc ('\n', fptmp);
1223   mutt_write_mime_body (a, fptmp);
1224   m_fclose(&fptmp);
1225
1226   if ((thepid =
1227        smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1228                              fileno (fpout), fileno (smimeerr),
1229                              smimeinfile, certfile)) == -1) {
1230     m_fclose(&smimeerr);
1231     mutt_unlink (smimeinfile);
1232     mutt_unlink (certfile);
1233     return (NULL);
1234   }
1235
1236   m_fclose(&smimein);
1237
1238   mutt_wait_filter (thepid);
1239   mutt_unlink (smimeinfile);
1240   mutt_unlink (certfile);
1241
1242   fflush (fpout);
1243   rewind (fpout);
1244   empty = (fgetc (fpout) == EOF);
1245   m_fclose(&fpout);
1246
1247   fflush (smimeerr);
1248   rewind (smimeerr);
1249   while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1250     err = 1;
1251     fputs (buf, stdout);
1252   }
1253   m_fclose(&smimeerr);
1254
1255   /* pause if there is any error output from SMIME */
1256   if (err)
1257     mutt_any_key_to_continue (NULL);
1258
1259   if (empty) {
1260     /* fatal error while trying to encrypt message */
1261     if (!err)
1262       mutt_any_key_to_continue _("No output from OpenSSL..");
1263
1264     mutt_unlink (tempfile);
1265     return (NULL);
1266   }
1267
1268   t = body_new();
1269   t->type = TYPEAPPLICATION;
1270   t->subtype = m_strdup("x-pkcs7-mime");
1271   parameter_setval(&t->parameter, "name", "smime.p7m");
1272   parameter_setval(&t->parameter, "smime-type", "enveloped-data");
1273   t->encoding = ENCBASE64;      /* The output of OpenSSL SHOULD be binary */
1274   t->use_disp = 1;
1275   t->disposition = DISPATTACH;
1276   t->d_filename = m_strdup("smime.p7m");
1277   t->filename = m_strdup(tempfile);
1278   t->unlink = 1;                /*delete after sending the message */
1279   t->parts = 0;
1280   t->next = 0;
1281
1282   return (t);
1283 }
1284
1285
1286
1287
1288 BODY *smime_sign_message (BODY * a)
1289 {
1290   BODY *t;
1291   char buffer[LONG_STRING];
1292   char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1293   FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1294   int err = 0;
1295   int empty = 0;
1296   pid_t thepid;
1297   char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1298
1299   if (!intermediates) {
1300     mutt_message (_("Warning: Intermediate certificate not found."));
1301     intermediates = SmimeDefaultKey;    /* so openssl won't complain in any case */
1302   }
1303
1304   convert_to_7bit (a);          /* Signed data _must_ be in 7-bit format. */
1305
1306   sfp = m_tempfile (filetosign, sizeof(filetosign), NONULL(Tempdir), NULL);
1307   if (!sfp) {
1308     mutt_perror (filetosign);
1309     return NULL;
1310   }
1311
1312   smimeout = m_tempfile (signedfile, sizeof(signedfile), NONULL(Tempdir), NULL);
1313   if (!smimeout) {
1314     mutt_perror (signedfile);
1315     m_fclose(&sfp);
1316     mutt_unlink (filetosign);
1317     return NULL;
1318   }
1319
1320   mutt_write_mime_header (a, sfp);
1321   fputc ('\n', sfp);
1322   mutt_write_mime_body (a, sfp);
1323   m_fclose(&sfp);
1324
1325
1326
1327   snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1328             NONULL (SmimeKeys), SmimeDefaultKey);
1329
1330   snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1331             NONULL (SmimeCertificates), SmimeDefaultKey);
1332
1333   snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1334             NONULL (SmimeCertificates), intermediates);
1335
1336
1337
1338   if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1339                                    -1, fileno (smimeout), -1,
1340                                    filetosign)) == -1) {
1341     mutt_perror (_("Can't open OpenSSL subprocess!"));
1342
1343     m_fclose(&smimeout);
1344     mutt_unlink (signedfile);
1345     mutt_unlink (filetosign);
1346     return NULL;
1347   }
1348   fputs (SmimePass, smimein);
1349   fputc ('\n', smimein);
1350   m_fclose(&smimein);
1351
1352
1353   mutt_wait_filter (thepid);
1354
1355   /* check for errors from OpenSSL */
1356   err = 0;
1357   fflush (smimeerr);
1358   rewind (smimeerr);
1359   while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1360     err = 1;
1361     fputs (buffer, stdout);
1362   }
1363   m_fclose(&smimeerr);
1364
1365
1366   fflush (smimeout);
1367   rewind (smimeout);
1368   empty = (fgetc (smimeout) == EOF);
1369   m_fclose(&smimeout);
1370
1371   mutt_unlink (filetosign);
1372
1373
1374   if (err)
1375     mutt_any_key_to_continue (NULL);
1376
1377   if (empty) {
1378     mutt_any_key_to_continue _("No output from OpenSSL...");
1379
1380     mutt_unlink (signedfile);
1381     return (NULL);              /* fatal error while signing */
1382   }
1383
1384   t = body_new();
1385   t->type = TYPEMULTIPART;
1386   t->subtype = m_strdup("signed");
1387   t->encoding = ENC7BIT;
1388   t->use_disp = 0;
1389   t->disposition = DISPINLINE;
1390
1391   parameter_set_boundary(&t->parameter);
1392   /* check if this can be extracted from private key somehow.... */
1393   parameter_setval(&t->parameter, "micalg", "sha1");
1394   parameter_setval(&t->parameter, "protocol",
1395                    "application/x-pkcs7-signature");
1396
1397   t->parts = a;
1398   a = t;
1399
1400   t->parts->next = body_new();
1401   t = t->parts->next;
1402   t->type = TYPEAPPLICATION;
1403   t->subtype = m_strdup("x-pkcs7-signature");
1404   t->filename = m_strdup(signedfile);
1405   t->d_filename = m_strdup("smime.p7s");
1406   t->use_disp = 1;
1407   t->disposition = DISPATTACH;
1408   t->encoding = ENCBASE64;
1409   t->unlink = 1;                /* ok to remove this file after sending. */
1410
1411   return (a);
1412
1413 }
1414
1415
1416 /*
1417  *    Handling S/MIME - bodies.
1418  */
1419
1420
1421 static
1422 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1423                            FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1424                            int smimeerrfd, const char *fname,
1425                            const char *sig_fname, int opaque)
1426 {
1427   return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1428                        smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1429                        (opaque ? SmimeVerifyOpaqueCommand :
1430                         SmimeVerifyCommand));
1431 }
1432
1433
1434 static
1435 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1436                             FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1437                             int smimeerrfd, const char *fname)
1438 {
1439   return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1440                        smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1441                        SmimeCertToUse, NULL, SmimeDecryptCommand);
1442 }
1443
1444
1445
1446 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1447 {
1448   char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1449   FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1450   pid_t thepid;
1451   int badsig = -1;
1452
1453   long tmpoffset = 0;
1454   ssize_t tmplength = 0;
1455   int origType = sigbdy->type;
1456   char *savePrefix = NULL;
1457
1458
1459   snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1460
1461   /* decode to a tempfile, saving the original destination */
1462   fp = s->fpout;
1463   if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1464     mutt_perror (signedfile);
1465     return -1;
1466   }
1467   /* decoding the attachment changes the size and offset, so save a copy
1468    * of the "real" values now, and restore them after processing
1469    */
1470   tmplength = sigbdy->length;
1471   tmpoffset = sigbdy->offset;
1472
1473   /* if we are decoding binary bodies, we don't want to prefix each
1474    * line with the prefix or else the data will get corrupted.
1475    */
1476   savePrefix = s->prefix;
1477   s->prefix = NULL;
1478
1479   mutt_decode_attachment (sigbdy, s);
1480
1481   sigbdy->length = ftello (s->fpout);
1482   sigbdy->offset = 0;
1483   m_fclose(&s->fpout);
1484
1485   /* restore final destination and substitute the tempfile for input */
1486   s->fpout = fp;
1487   fp = s->fpin;
1488   s->fpin = fopen (signedfile, "r");
1489
1490   /* restore the prefix */
1491   s->prefix = savePrefix;
1492
1493   sigbdy->type = origType;
1494
1495   smimeerr = m_tempfile(smimeerrfile, sizeof(smimeerrfile), NONULL(Tempdir), NULL);
1496   if (!smimeerr) {
1497     mutt_perror (smimeerrfile);
1498     mutt_unlink (signedfile);
1499     return -1;
1500   }
1501
1502   crypt_current_time (s, "OpenSSL");
1503
1504   if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1505                                      -1, -1, fileno (smimeerr),
1506                                      tempfile, signedfile, 0)) != -1) {
1507     m_fclose(&smimeout);
1508
1509     if (mutt_wait_filter (thepid))
1510       badsig = -1;
1511     else {
1512       char *line = NULL;
1513       int lineno = 0;
1514       ssize_t linelen;
1515
1516       fflush (smimeerr);
1517       rewind (smimeerr);
1518
1519       line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1520       if (linelen && !m_strcasecmp(line, "verification successful"))
1521         badsig = 0;
1522
1523       p_delete(&line);
1524     }
1525   }
1526
1527   fflush (smimeerr);
1528   rewind (smimeerr);
1529   mutt_copy_stream (smimeerr, s->fpout);
1530   m_fclose(&smimeerr);
1531
1532   state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1533
1534   mutt_unlink (signedfile);
1535   mutt_unlink (smimeerrfile);
1536
1537   sigbdy->length = tmplength;
1538   sigbdy->offset = tmpoffset;
1539
1540   /* restore the original source stream */
1541   m_fclose(&s->fpin);
1542   s->fpin = fp;
1543
1544
1545   return badsig;
1546 }
1547
1548
1549
1550
1551
1552 /*
1553   This handles application/pkcs7-mime which can either be a signed
1554   or an encrypted message.
1555 */
1556
1557 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1558 {
1559   int len = 0;
1560   int c;
1561   long last_pos;
1562   char buf[HUGE_STRING];
1563   char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1564   char tmpfname[_POSIX_PATH_MAX];
1565   char tmptmpfname[_POSIX_PATH_MAX];
1566   FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1567   FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1568   struct stat info;
1569   BODY *p = NULL;
1570   pid_t thepid = -1;
1571   unsigned int type = mutt_is_application_smime (m);
1572
1573   if (!(type & APPLICATION_SMIME))
1574     return NULL;
1575
1576   smimeout = m_tempfile (outfile, sizeof(outfile), NONULL(Tempdir), NULL);
1577   if (!smimeout) {
1578     mutt_perror (outfile);
1579     return NULL;
1580   }
1581
1582   smimeerr = m_tempfile(errfile, sizeof(errfile), NONULL(Tempdir), NULL);
1583   if (!smimeerr) {
1584     mutt_perror (errfile);
1585     m_fclose(&smimeout);
1586     return NULL;
1587   }
1588   mutt_unlink (errfile);
1589
1590   tmpfp = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1591   if (!tmpfp) {
1592     mutt_perror (tmpfname);
1593     m_fclose(&smimeout);
1594     m_fclose(&smimeerr);
1595     return NULL;
1596   }
1597
1598   fseeko (s->fpin, m->offset, 0);
1599   last_pos = m->offset;
1600
1601   mutt_copy_bytes (s->fpin, tmpfp, m->length);
1602   m_fclose(&tmpfp);
1603
1604   if ((type & ENCRYPT) &&
1605       (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1606                                       fileno (smimeout), fileno (smimeerr),
1607                                       tmpfname)) == -1) {
1608     m_fclose(&smimeout);
1609     mutt_unlink (tmpfname);
1610     if (s->flags & M_DISPLAY)
1611       state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1612     return NULL;
1613   }
1614   else if ((type & SIGNOPAQUE) &&
1615            (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1616                                           fileno (smimeout),
1617                                           fileno (smimeerr), NULL, tmpfname,
1618                                           SIGNOPAQUE)) == -1) {
1619     m_fclose(&smimeout);
1620     mutt_unlink (tmpfname);
1621     if (s->flags & M_DISPLAY)
1622       state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1623     return NULL;
1624   }
1625
1626
1627   if (type & ENCRYPT) {
1628     if (!smime_valid_passphrase ())
1629       smime_void_passphrase ();
1630     fputs (SmimePass, smimein);
1631     fputc ('\n', smimein);
1632   }
1633
1634   m_fclose(&smimein);
1635
1636   mutt_wait_filter (thepid);
1637   mutt_unlink (tmpfname);
1638
1639
1640   if (s->flags & M_DISPLAY) {
1641     rewind (smimeerr);
1642
1643     if ((c = fgetc (smimeerr)) != EOF) {
1644       ungetc (c, smimeerr);
1645
1646       crypt_current_time (s, "OpenSSL");
1647       mutt_copy_stream (smimeerr, s->fpout);
1648       state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1649     }
1650
1651     if (type & ENCRYPT)
1652       state_attach_puts (_("[-- The following data is S/MIME"
1653                            " encrypted --]\n"), s);
1654     else
1655       state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1656                          s);
1657   }
1658
1659   if (smimeout) {
1660     fflush (smimeout);
1661     rewind (smimeout);
1662
1663     if (outFile)
1664       fpout = outFile;
1665     else {
1666       fpout = m_tempfile (tmptmpfname, sizeof(tmptmpfname), NONULL(Tempdir), NULL);
1667       if (!fpout) {
1668         mutt_perror (tmptmpfname);
1669         m_fclose(&smimeout);
1670         return NULL;
1671       }
1672     }
1673     while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1674       len = m_strlen(buf);
1675       if (len > 1 && buf[len - 2] == '\r') {
1676         buf[len - 2] = '\n';
1677         buf[len - 1] = '\0';
1678       }
1679       fputs (buf, fpout);
1680     }
1681     fflush (fpout);
1682     rewind (fpout);
1683
1684
1685     if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1686       fstat (fileno (fpout), &info);
1687       p->length = info.st_size - p->offset;
1688
1689       mutt_parse_part (fpout, p);
1690       if (s->fpout) {
1691         rewind (fpout);
1692         tmpfp_buffer = s->fpin;
1693         s->fpin = fpout;
1694         mutt_body_handler (p, s);
1695         s->fpin = tmpfp_buffer;
1696       }
1697
1698     }
1699     m_fclose(&smimeout);
1700     mutt_unlink (outfile);
1701
1702     if (!outFile) {
1703       m_fclose(&fpout);
1704       mutt_unlink (tmptmpfname);
1705     }
1706     fpout = NULL;
1707   }
1708
1709   if (s->flags & M_DISPLAY) {
1710     if (type & ENCRYPT)
1711       state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1712     else
1713       state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1714   }
1715
1716   if (type & SIGNOPAQUE) {
1717     char *line = NULL;
1718     int lineno = 0;
1719     ssize_t linelen;
1720
1721     rewind (smimeerr);
1722
1723     line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1724     if (linelen && !m_strcasecmp(line, "verification successful"))
1725       m->goodsig = 1;
1726     p_delete(&line);
1727   }
1728   else {
1729     m->goodsig = p->goodsig;
1730     m->badsig = p->badsig;
1731   }
1732   m_fclose(&smimeerr);
1733
1734   return (p);
1735 }
1736
1737
1738
1739
1740
1741 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1742 {
1743
1744
1745   char tempfile[_POSIX_PATH_MAX];
1746   STATE s;
1747   long tmpoffset = b->offset;
1748   ssize_t tmplength = b->length;
1749   int origType = b->type;
1750   FILE *tmpfp = NULL;
1751   int rv = 0;
1752
1753   if (!mutt_is_application_smime (b))
1754     return -1;
1755
1756   if (b->parts)
1757     return -1;
1758
1759   p_clear(&s, 1);
1760   s.fpin = fpin;
1761   fseeko (s.fpin, b->offset, 0);
1762
1763   tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
1764   if (!tmpfp) {
1765     mutt_perror (_("Can't create temporary file"));
1766     return (-1);
1767   }
1768
1769   mutt_unlink (tempfile);
1770   s.fpout = tmpfp;
1771   mutt_decode_attachment (b, &s);
1772   fflush (tmpfp);
1773   b->length = ftello (s.fpout);
1774   b->offset = 0;
1775   rewind (tmpfp);
1776   s.fpin = tmpfp;
1777   s.fpout = 0;
1778
1779   *fpout = m_tempfile (tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
1780   if (!*fpout) {
1781     mutt_perror (_("Can't create temporary file"));
1782     rv = -1;
1783     goto bail;
1784   }
1785   mutt_unlink (tempfile);
1786
1787   if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1788     rv = -1;
1789     goto bail;
1790   }
1791
1792   (*cur)->goodsig = b->goodsig;
1793   (*cur)->badsig  = b->badsig;
1794
1795 bail:
1796   b->type = origType;
1797   b->length = tmplength;
1798   b->offset = tmpoffset;
1799
1800   m_fclose(&tmpfp);
1801   if (*fpout)
1802     rewind (*fpout);
1803   return (rv);
1804 }
1805
1806
1807 int smime_application_smime_handler (BODY * m, STATE * s)
1808 {
1809   return smime_handle_entity (m, s, NULL) ? 0 : -1;
1810 }
1811
1812 int smime_send_menu (HEADER * msg, int *redraw)
1813 {
1814   char *p;
1815
1816   switch (mutt_multi_choice
1817           (_("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1818            _("eswabfc"))) {
1819   case 1:                      /* (e)ncrypt */
1820     msg->security |= ENCRYPT;
1821     msg->security &= ~SIGN;
1822     break;
1823
1824   case 3:                      /* encrypt (w)ith */
1825     {
1826       int choice = 0;
1827       msg->security |= ENCRYPT;
1828
1829       do {
1830         /* I use "dra" because "123" is recognized anyway */
1831         switch (mutt_multi_choice (_("Choose algorithm family:"
1832                                      " 1: DES, 2: RC2, 3: AES,"
1833                                      " or (c)lear? "), _("drac"))) {
1834           case 1:
1835             switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
1836                                                 _("dt"))) {
1837               case 1:
1838                 m_strreplace(&SmimeCryptAlg, "des");
1839                 break;
1840               case 2:
1841                 m_strreplace(&SmimeCryptAlg, "des3");
1842                 break;
1843             }
1844             break;
1845
1846           case 2:
1847             switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
1848                                                 _("468"))) {
1849               case 1:
1850                 m_strreplace(&SmimeCryptAlg, "rc2-40");
1851                 break;
1852               case 2:
1853                 m_strreplace(&SmimeCryptAlg, "rc2-64");
1854                 break;
1855               case 3:
1856                 m_strreplace(&SmimeCryptAlg, "rc2-128");
1857                 break;
1858             }
1859             break;
1860
1861           case 3:
1862             switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
1863                                                 _("895"))) {
1864               case 1:
1865                 m_strreplace(&SmimeCryptAlg, "aes128");
1866                 break;
1867               case 2:
1868                 m_strreplace(&SmimeCryptAlg, "aes192");
1869                 break;
1870               case 3:
1871                 m_strreplace(&SmimeCryptAlg, "aes256");
1872                 break;
1873             }
1874             break;
1875
1876           case 4: /* (c)lear */
1877             p_delete(&SmimeCryptAlg);
1878             /* fallback */
1879           case -1: /* Ctrl-G or Enter */
1880             choice = 0;
1881             break;
1882         }
1883       } while (choice == -1);
1884     }
1885     break;
1886
1887   case 2:                      /* (s)ign */
1888
1889     if (!SmimeDefaultKey)
1890       mutt_message (_("Can't sign: No key specified. Use Sign As."));
1891
1892     else {
1893       msg->security |= SIGN;
1894       msg->security &= ~ENCRYPT;
1895     }
1896     break;
1897
1898   case 4:                      /* sign (a)s */
1899
1900     if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1901       m_strreplace(&SmimeDefaultKey, p);
1902
1903       msg->security |= SIGN;
1904
1905       /* probably need a different passphrase */
1906       crypt_smime_void_passphrase ();
1907     }
1908
1909     *redraw = REDRAW_FULL;
1910     break;
1911
1912   case 5:                      /* (b)oth */
1913     msg->security |= (ENCRYPT | SIGN);
1914     break;
1915
1916   case 6:                      /* (f)orget it */
1917   case 7:                      /* (c)lear */
1918     msg->security = 0;
1919     break;
1920   }
1921
1922   if (msg->security && msg->security != APPLICATION_SMIME)
1923     msg->security |= APPLICATION_SMIME;
1924   else
1925     msg->security = 0;
1926
1927   return (msg->security);
1928 }