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