Clean up code and use m_tempfile instead of mutt_mktemp
[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     mutt_FormatString (dest, destlen, ifstring, _mutt_fmt_smime_command,
223                        data, 0);
224   else if (flags & M_FORMAT_OPTIONAL)
225     mutt_FormatString (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   mutt_FormatString (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   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     fclose (idx);
403
404     /* Make Helpstring */
405     helpstr[0] = 0;
406     mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_SMIME, OP_EXIT);
407     strcat (helpstr, buf);      /* __STRCAT_CHECKED__ */
408     mutt_make_help (buf, sizeof (buf), _("Select  "), MENU_SMIME,
409                     OP_GENERIC_SELECT_ENTRY);
410     strcat (helpstr, buf);      /* __STRCAT_CHECKED__ */
411     mutt_make_help (buf, sizeof (buf), _("Help"), MENU_SMIME, OP_HELP);
412     strcat (helpstr, buf);      /* __STRCAT_CHECKED__ */
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     safe_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   const char *fqdn = mutt_fqdn (1);
712
713   for (i = 0; i < 3; i++) {
714     switch (i) {
715     case 0:
716       p = to;
717       break;
718     case 1:
719       p = cc;
720       break;
721     case 2:
722       p = bcc;
723       break;
724     default:
725       abort ();
726     }
727
728     *last = address_list_dup (p);
729     while (*last)
730       last = &((*last)->next);
731   }
732
733   if (fqdn)
734     rfc822_qualify (tmp, fqdn);
735
736   address_list_uniq(tmp);
737
738   for (p = tmp; p; p = p->next) {
739     char buf[LONG_STRING];
740
741     q = p;
742
743     if ((keyID = smime_get_field_from_db (q->mailbox, NULL, 1, 1)) == NULL) {
744       snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
745       keyID = smime_ask_for_key (buf, q->mailbox, 1);
746     }
747     if (!keyID) {
748       mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
749       p_delete(&keylist);
750       address_list_wipe(&tmp);
751       address_list_wipe(&addr);
752       return NULL;
753     }
754
755     keylist_size += m_strlen(keyID) + 2;
756     p_realloc(&keylist, keylist_size);
757     sprintf (keylist + keylist_used, "%s\n", keyID);    /* __SPRINTF_CHECKED__ */
758     keylist_used = m_strlen(keylist);
759
760     address_list_wipe(&addr);
761
762   }
763   address_list_wipe(&tmp);
764   return (keylist);
765 }
766
767
768
769
770
771
772 static int smime_handle_cert_email (char *certificate, char *mailbox,
773                                     int copy, char ***buffer, int *num)
774 {
775   FILE *fpout = NULL, *fperr = NULL;
776   char tmpfname[_POSIX_PATH_MAX];
777   char email[STRING];
778   int ret = -1, count = 0;
779   pid_t thepid;
780
781   fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
782   if (!fperr) {
783     mutt_perror (tmpfname);
784     return 1;
785   }
786   mutt_unlink (tmpfname);
787
788   fpout = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
789   if (!fpout) {
790     fclose (fperr);
791     mutt_perror (tmpfname);
792     return 1;
793   }
794   mutt_unlink (tmpfname);
795
796   if ((thepid = smime_invoke (NULL, NULL, NULL,
797                               -1, fileno (fpout), fileno (fperr),
798                               certificate, NULL, NULL, NULL, NULL, NULL,
799                               SmimeGetCertEmailCommand)) == -1) {
800     mutt_message (_("Error: unable to create OpenSSL subprocess!"));
801     fclose (fperr);
802     fclose (fpout);
803     return 1;
804   }
805
806   mutt_wait_filter (thepid);
807
808   fflush (fpout);
809   rewind (fpout);
810   rewind (fperr);
811   fflush (fperr);
812
813
814   while ((fgets (email, sizeof (email), fpout))) {
815     *(email + m_strlen(email) - 1) = '\0';
816     if (m_strncasecmp(email, mailbox, m_strlen(mailbox)) == 0)
817       ret = 1;
818
819     ret = ret < 0 ? 0 : ret;
820     count++;
821   }
822
823   if (ret == -1) {
824     mutt_endwin (NULL);
825     mutt_copy_stream (fperr, stdout);
826     mutt_any_key_to_continue (_
827                               ("Error: unable to create OpenSSL subprocess!"));
828     ret = 1;
829   }
830   else if (!ret)
831     ret = 1;
832   else
833     ret = 0;
834
835   if (copy && buffer && num) {
836     (*num) = count;
837     *buffer = p_new(char *, count);
838     count = 0;
839
840     rewind (fpout);
841     while ((fgets (email, sizeof (email), fpout))) {
842       *(email + m_strlen(email) - 1) = '\0';
843       (*buffer)[count] = p_dupstr(email, m_strlen(email));
844       count++;
845     }
846   }
847   else if (copy)
848     ret = 2;
849
850   fclose (fpout);
851   fclose (fperr);
852
853   return ret;
854 }
855
856
857
858 static char *smime_extract_certificate (char *infile)
859 {
860   FILE *fpout = NULL, *fperr = NULL;
861   char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
862   char tmpfname[_POSIX_PATH_MAX];
863   pid_t thepid;
864   int empty;
865
866
867   fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
868   if (!fperr) {
869     mutt_perror (tmpfname);
870     return NULL;
871   }
872   mutt_unlink (tmpfname);
873
874   fpout = m_tempfile (pk7out, sizeof(tmpfname), NONULL(Tempdir), NULL);
875   if (!fpout) {
876     fclose (fperr);
877     mutt_perror (pk7out);
878     return NULL;
879   }
880
881   /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
882      extract the full set of certificates directly.
883    */
884   if ((thepid = smime_invoke (NULL, NULL, NULL,
885                               -1, fileno (fpout), fileno (fperr),
886                               infile, NULL, NULL, NULL, NULL, NULL,
887                               SmimePk7outCommand)) == -1) {
888     mutt_any_key_to_continue (_
889                               ("Error: unable to create OpenSSL subprocess!"));
890     fclose (fperr);
891     fclose (fpout);
892     mutt_unlink (pk7out);
893     return NULL;
894   }
895
896   mutt_wait_filter (thepid);
897
898
899   fflush (fpout);
900   rewind (fpout);
901   rewind (fperr);
902   fflush (fperr);
903
904   empty = (fgetc (fpout) == EOF);
905
906   fclose (fpout);
907
908   if (empty) {
909     mutt_perror (pk7out);
910     mutt_copy_stream (fperr, stdout);
911     fclose (fperr);
912     mutt_unlink (pk7out);
913     return NULL;
914   }
915
916   fpout = m_tempfile (certfile, sizeof(certfile), NONULL(Tempdir), NULL);
917   if (!fpout) {
918     fclose (fperr);
919     mutt_unlink (pk7out);
920     mutt_perror (certfile);
921     return NULL;
922   }
923
924   /* Step 2: Extract the certificates from a PKCS#7 structure.
925    */
926   if ((thepid = smime_invoke (NULL, NULL, NULL,
927                               -1, fileno (fpout), fileno (fperr),
928                               pk7out, NULL, NULL, NULL, NULL, NULL,
929                               SmimeGetCertCommand)) == -1) {
930     mutt_any_key_to_continue (_
931                               ("Error: unable to create OpenSSL subprocess!"));
932     fclose (fperr);
933     fclose (fpout);
934     mutt_unlink (pk7out);
935     mutt_unlink (certfile);
936     return NULL;
937   }
938
939   mutt_wait_filter (thepid);
940
941   mutt_unlink (pk7out);
942
943   fflush (fpout);
944   rewind (fpout);
945   rewind (fperr);
946   fflush (fperr);
947   empty = (fgetc (fpout) == EOF);
948   if (empty) {
949     mutt_copy_stream (fperr, stdout);
950     fclose (fpout);
951     fclose (fperr);
952     mutt_unlink (certfile);
953     return NULL;
954   }
955
956   fclose (fpout);
957   fclose (fperr);
958
959   return m_strdup(certfile);
960 }
961
962 static char *smime_extract_signer_certificate (char *infile)
963 {
964   FILE *fpout = NULL, *fperr = NULL;
965   char pk7out[_POSIX_PATH_MAX], certfile[_POSIX_PATH_MAX];
966   char tmpfname[_POSIX_PATH_MAX];
967   pid_t thepid;
968
969   fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
970   if (!fperr) {
971     mutt_perror (tmpfname);
972     return NULL;
973   }
974   mutt_unlink (tmpfname);
975
976   m_tempfile (certfile, sizeof(certfile), NONULL(Tempdir), NULL);
977   if (!fpout) {
978     fclose (fperr);
979     mutt_perror (certfile);
980     return NULL;
981   }
982
983   fclose (fpout);
984
985   /* Extract signer's certificate
986    */
987   if ((thepid = smime_invoke (NULL, NULL, NULL,
988                               -1, -1, fileno (fperr),
989                               infile, NULL, NULL, NULL, certfile, NULL,
990                               SmimeGetSignerCertCommand)) == -1) {
991     mutt_any_key_to_continue (_
992                               ("Error: unable to create OpenSSL subprocess!"));
993     fclose (fperr);
994     mutt_unlink (pk7out);
995     mutt_unlink (certfile);
996     return NULL;
997   }
998
999   mutt_wait_filter (thepid);
1000
1001   rewind (fperr);
1002   fflush (fperr);
1003
1004   mutt_endwin (NULL);
1005   mutt_copy_stream (fperr, stdout);
1006   fclose (fperr);
1007   mutt_any_key_to_continue (NULL);
1008   mutt_unlink (certfile);
1009   return NULL;
1010 }
1011
1012 /* Add a certificate and update index file (externally). */
1013
1014 void smime_invoke_import (char *infile, char *mailbox __attribute__ ((unused)))
1015 {
1016   char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1017   FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1018   pid_t thepid = -1;
1019
1020  fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1021   if (!fperr) {
1022     mutt_perror (tmpfname);
1023     return;
1024   }
1025   mutt_unlink (tmpfname);
1026
1027   fpout = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1028   if (!fpout) {
1029     fclose (fperr);
1030     mutt_perror (tmpfname);
1031     return;
1032   }
1033   mutt_unlink (tmpfname);
1034
1035   buf[0] = '\0';
1036   if (option (OPTASKCERTLABEL))
1037     mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1038
1039   mutt_endwin (NULL);
1040   if ((certfile = smime_extract_certificate (infile))) {
1041     mutt_endwin (NULL);
1042
1043     if ((thepid = smime_invoke (&smimein, NULL, NULL,
1044                                 -1, fileno (fpout), fileno (fperr),
1045                                 certfile, NULL, NULL, NULL, NULL, NULL,
1046                                 SmimeImportCertCommand)) == -1) {
1047       mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1048       return;
1049     }
1050     fputs (buf, smimein);
1051     fputc ('\n', smimein);
1052     fclose (smimein);
1053
1054     mutt_wait_filter (thepid);
1055
1056     mutt_unlink (certfile);
1057     p_delete(&certfile);
1058   }
1059
1060   fflush (fpout);
1061   rewind (fpout);
1062   fflush (fperr);
1063   rewind (fperr);
1064
1065   mutt_copy_stream (fpout, stdout);
1066   mutt_copy_stream (fperr, stdout);
1067
1068   fclose (fpout);
1069   fclose (fperr);
1070
1071 }
1072
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 (tempfname);
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   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 (tempfile);
1184     return NULL;
1185   }
1186
1187   smimeerr = m_tempfile (smimeerrfile, sizeof(smimeerrfile), NONULL(Tempdir), NULL);
1188   if (!smimeerr) {
1189     mutt_perror (smimeerrfile);
1190     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     fclose (fpout);
1201     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   fclose (fptmp);
1225
1226   if ((thepid =
1227        smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1228                              fileno (fpout), fileno (smimeerr),
1229                              smimeinfile, certfile)) == -1) {
1230     fclose (smimeerr);
1231     mutt_unlink (smimeinfile);
1232     mutt_unlink (certfile);
1233     return (NULL);
1234   }
1235
1236   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   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   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     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   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     fclose (smimeout);
1344     mutt_unlink (signedfile);
1345     mutt_unlink (filetosign);
1346     return NULL;
1347   }
1348   fputs (SmimePass, smimein);
1349   fputc ('\n', smimein);
1350   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   fclose (smimeerr);
1364
1365
1366   fflush (smimeout);
1367   rewind (smimeout);
1368   empty = (fgetc (smimeout) == EOF);
1369   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   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     fflush (smimeout);
1508     fclose (smimeout);
1509
1510     if (mutt_wait_filter (thepid))
1511       badsig = -1;
1512     else {
1513       char *line = NULL;
1514       int lineno = 0;
1515       ssize_t linelen;
1516
1517       fflush (smimeerr);
1518       rewind (smimeerr);
1519
1520       line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1521       if (linelen && !m_strcasecmp(line, "verification successful"))
1522         badsig = 0;
1523
1524       p_delete(&line);
1525     }
1526   }
1527
1528   fflush (smimeerr);
1529   rewind (smimeerr);
1530   mutt_copy_stream (smimeerr, s->fpout);
1531   fclose (smimeerr);
1532
1533   state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1534
1535   mutt_unlink (signedfile);
1536   mutt_unlink (smimeerrfile);
1537
1538   sigbdy->length = tmplength;
1539   sigbdy->offset = tmpoffset;
1540
1541   /* restore the original source stream */
1542   fclose (s->fpin);
1543   s->fpin = fp;
1544
1545
1546   return badsig;
1547 }
1548
1549
1550
1551
1552
1553 /*
1554   This handles application/pkcs7-mime which can either be a signed
1555   or an encrypted message.
1556 */
1557
1558 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1559 {
1560   int len = 0;
1561   int c;
1562   long last_pos;
1563   char buf[HUGE_STRING];
1564   char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1565   char tmpfname[_POSIX_PATH_MAX];
1566   char tmptmpfname[_POSIX_PATH_MAX];
1567   FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1568   FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1569   struct stat info;
1570   BODY *p = NULL;
1571   pid_t thepid = -1;
1572   unsigned int type = mutt_is_application_smime (m);
1573
1574   if (!(type & APPLICATION_SMIME))
1575     return NULL;
1576
1577   smimeout = m_tempfile (outfile, sizeof(outfile), NONULL(Tempdir), NULL);
1578   if (!smimeout) {
1579     mutt_perror (outfile);
1580     return NULL;
1581   }
1582
1583   smimeerr = m_tempfile(errfile, sizeof(errfile), NONULL(Tempdir), NULL);
1584   if (!smimeerr) {
1585     mutt_perror (errfile);
1586     fclose (smimeout);
1587     return NULL;
1588   }
1589   mutt_unlink (errfile);
1590
1591   tmpfp = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1592   if (!tmpfp) {
1593     mutt_perror (tmpfname);
1594     fclose (smimeout);
1595     fclose (smimeerr);
1596     return NULL;
1597   }
1598
1599   fseeko (s->fpin, m->offset, 0);
1600   last_pos = m->offset;
1601
1602   mutt_copy_bytes (s->fpin, tmpfp, m->length);
1603
1604   fflush (tmpfp);
1605   fclose (tmpfp);
1606
1607   if ((type & ENCRYPT) &&
1608       (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1609                                       fileno (smimeout), fileno (smimeerr),
1610                                       tmpfname)) == -1) {
1611     fclose (smimeout);
1612     smimeout = NULL;
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     fclose (smimeout);
1624     smimeout = NULL;
1625     mutt_unlink (tmpfname);
1626     if (s->flags & M_DISPLAY)
1627       state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1628     return NULL;
1629   }
1630
1631
1632   if (type & ENCRYPT) {
1633     if (!smime_valid_passphrase ())
1634       smime_void_passphrase ();
1635     fputs (SmimePass, smimein);
1636     fputc ('\n', smimein);
1637   }
1638
1639   fclose (smimein);
1640
1641   mutt_wait_filter (thepid);
1642   mutt_unlink (tmpfname);
1643
1644
1645   if (s->flags & M_DISPLAY) {
1646     rewind (smimeerr);
1647
1648     if ((c = fgetc (smimeerr)) != EOF) {
1649       ungetc (c, smimeerr);
1650
1651       crypt_current_time (s, "OpenSSL");
1652       mutt_copy_stream (smimeerr, s->fpout);
1653       state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1654     }
1655
1656     if (type & ENCRYPT)
1657       state_attach_puts (_("[-- The following data is S/MIME"
1658                            " encrypted --]\n"), s);
1659     else
1660       state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1661                          s);
1662   }
1663
1664   if (smimeout) {
1665     fflush (smimeout);
1666     rewind (smimeout);
1667
1668     if (outFile)
1669       fpout = outFile;
1670     else {
1671       fpout = m_tempfile (tmptmpfname, sizeof(tmptmpfname), NONULL(Tempdir), NULL);
1672       if (!fpout) {
1673         mutt_perror (tmptmpfname);
1674         fclose (smimeout);
1675         return NULL;
1676       }
1677     }
1678     while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1679       len = m_strlen(buf);
1680       if (len > 1 && buf[len - 2] == '\r') {
1681         buf[len - 2] = '\n';
1682         buf[len - 1] = '\0';
1683       }
1684       fputs (buf, fpout);
1685     }
1686     fflush (fpout);
1687     rewind (fpout);
1688
1689
1690     if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1691       fstat (fileno (fpout), &info);
1692       p->length = info.st_size - p->offset;
1693
1694       mutt_parse_part (fpout, p);
1695       if (s->fpout) {
1696         rewind (fpout);
1697         tmpfp_buffer = s->fpin;
1698         s->fpin = fpout;
1699         mutt_body_handler (p, s);
1700         s->fpin = tmpfp_buffer;
1701       }
1702
1703     }
1704     fclose (smimeout);
1705     smimeout = NULL;
1706     mutt_unlink (outfile);
1707
1708     if (!outFile) {
1709       fclose (fpout);
1710       mutt_unlink (tmptmpfname);
1711     }
1712     fpout = NULL;
1713   }
1714
1715   if (s->flags & M_DISPLAY) {
1716     if (type & ENCRYPT)
1717       state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1718     else
1719       state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1720   }
1721
1722   if (type & SIGNOPAQUE) {
1723     char *line = NULL;
1724     int lineno = 0;
1725     ssize_t linelen;
1726
1727     rewind (smimeerr);
1728
1729     line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1730     if (linelen && !m_strcasecmp(line, "verification successful"))
1731       m->goodsig = 1;
1732     p_delete(&line);
1733   }
1734   else {
1735     m->goodsig = p->goodsig;
1736     m->badsig = p->badsig;
1737   }
1738   fclose (smimeerr);
1739
1740   return (p);
1741 }
1742
1743
1744
1745
1746
1747 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1748 {
1749
1750
1751   char tempfile[_POSIX_PATH_MAX];
1752   STATE s;
1753   long tmpoffset = b->offset;
1754   ssize_t tmplength = b->length;
1755   int origType = b->type;
1756   FILE *tmpfp = NULL;
1757   int rv = 0;
1758
1759   if (!mutt_is_application_smime (b))
1760     return -1;
1761
1762   if (b->parts)
1763     return -1;
1764
1765   p_clear(&s, 1);
1766   s.fpin = fpin;
1767   fseeko (s.fpin, b->offset, 0);
1768
1769   tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
1770   if (!tmpfp) {
1771     mutt_perror (tempfile);
1772     return (-1);
1773   }
1774
1775   mutt_unlink (tempfile);
1776   s.fpout = tmpfp;
1777   mutt_decode_attachment (b, &s);
1778   fflush (tmpfp);
1779   b->length = ftello (s.fpout);
1780   b->offset = 0;
1781   rewind (tmpfp);
1782   s.fpin = tmpfp;
1783   s.fpout = 0;
1784
1785   *fpout = m_tempfile (tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
1786   if (!*fpout) {
1787     mutt_perror (tempfile);
1788     rv = -1;
1789     goto bail;
1790   }
1791   mutt_unlink (tempfile);
1792
1793   if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1794     rv = -1;
1795     goto bail;
1796   }
1797
1798   (*cur)->goodsig = b->goodsig;
1799   (*cur)->badsig  = b->badsig;
1800
1801 bail:
1802   b->type = origType;
1803   b->length = tmplength;
1804   b->offset = tmpoffset;
1805
1806   safe_fclose (&tmpfp);
1807   if (*fpout)
1808     rewind (*fpout);
1809   return (rv);
1810 }
1811
1812
1813 int smime_application_smime_handler (BODY * m, STATE * s)
1814 {
1815   return smime_handle_entity (m, s, NULL) ? 0 : -1;
1816 }
1817
1818 int smime_send_menu (HEADER * msg, int *redraw)
1819 {
1820   char *p;
1821
1822   switch (mutt_multi_choice
1823           (_("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1824            _("eswabfc"))) {
1825   case 1:                      /* (e)ncrypt */
1826     msg->security |= ENCRYPT;
1827     msg->security &= ~SIGN;
1828     break;
1829
1830   case 3:                      /* encrypt (w)ith */
1831     {
1832       int choice = 0;
1833       msg->security |= ENCRYPT;
1834
1835       do {
1836         /* I use "dra" because "123" is recognized anyway */
1837         switch (mutt_multi_choice (_("Choose algorithm family:"
1838                                      " 1: DES, 2: RC2, 3: AES,"
1839                                      " or (c)lear? "), _("drac"))) {
1840           case 1:
1841             switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
1842                                                 _("dt"))) {
1843               case 1:
1844                 m_strreplace(&SmimeCryptAlg, "des");
1845                 break;
1846               case 2:
1847                 m_strreplace(&SmimeCryptAlg, "des3");
1848                 break;
1849             }
1850             break;
1851
1852           case 2:
1853             switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
1854                                                 _("468"))) {
1855               case 1:
1856                 m_strreplace(&SmimeCryptAlg, "rc2-40");
1857                 break;
1858               case 2:
1859                 m_strreplace(&SmimeCryptAlg, "rc2-64");
1860                 break;
1861               case 3:
1862                 m_strreplace(&SmimeCryptAlg, "rc2-128");
1863                 break;
1864             }
1865             break;
1866
1867           case 3:
1868             switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
1869                                                 _("895"))) {
1870               case 1:
1871                 m_strreplace(&SmimeCryptAlg, "aes128");
1872                 break;
1873               case 2:
1874                 m_strreplace(&SmimeCryptAlg, "aes192");
1875                 break;
1876               case 3:
1877                 m_strreplace(&SmimeCryptAlg, "aes256");
1878                 break;
1879             }
1880             break;
1881
1882           case 4: /* (c)lear */
1883             p_delete(&SmimeCryptAlg);
1884             /* fallback */
1885           case -1: /* Ctrl-G or Enter */
1886             choice = 0;
1887             break;
1888         }
1889       } while (choice == -1);
1890     }
1891     break;
1892
1893   case 2:                      /* (s)ign */
1894
1895     if (!SmimeDefaultKey)
1896       mutt_message (_("Can't sign: No key specified. Use Sign As."));
1897
1898     else {
1899       msg->security |= SIGN;
1900       msg->security &= ~ENCRYPT;
1901     }
1902     break;
1903
1904   case 4:                      /* sign (a)s */
1905
1906     if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1907       m_strreplace(&SmimeDefaultKey, p);
1908
1909       msg->security |= SIGN;
1910
1911       /* probably need a different passphrase */
1912       crypt_smime_void_passphrase ();
1913     }
1914 #if 0
1915     else
1916       msg->security &= ~SIGN;
1917 #endif
1918
1919     *redraw = REDRAW_FULL;
1920     break;
1921
1922   case 5:                      /* (b)oth */
1923     msg->security |= (ENCRYPT | SIGN);
1924     break;
1925
1926   case 6:                      /* (f)orget it */
1927   case 7:                      /* (c)lear */
1928     msg->security = 0;
1929     break;
1930   }
1931
1932   if (msg->security && msg->security != APPLICATION_SMIME)
1933     msg->security |= APPLICATION_SMIME;
1934   else
1935     msg->security = 0;
1936
1937   return (msg->security);
1938 }