1248f31bf86f55abeee024417fc37980c616399c
[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   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   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);
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     m_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     m_fclose(&fperr);
802     m_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   m_fclose(&fpout);
851   m_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     m_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     m_fclose(&fperr);
891     m_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   m_fclose(&fpout);
907
908   if (empty) {
909     mutt_perror (pk7out);
910     mutt_copy_stream (fperr, stdout);
911     m_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     m_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     m_fclose(&fperr);
933     m_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     m_fclose(&fpout);
951     m_fclose(&fperr);
952     mutt_unlink (certfile);
953     return NULL;
954   }
955
956   m_fclose(&fpout);
957   m_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   int empty;
969
970   fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
971   if (!fperr) {
972     mutt_perror (tmpfname);
973     return NULL;
974   }
975   mutt_unlink (tmpfname);
976
977   m_tempfile (certfile, sizeof(certfile), NONULL(Tempdir), NULL);
978   if (!fpout) {
979     m_fclose(&fperr);
980     mutt_perror (certfile);
981     return NULL;
982   }
983
984   /* Extract signer's certificate
985    */
986   if ((thepid = smime_invoke (NULL, NULL, NULL,
987                               -1, -1, fileno (fperr),
988                               infile, NULL, NULL, NULL, certfile, NULL,
989                               SmimeGetSignerCertCommand)) == -1) {
990     mutt_any_key_to_continue (_
991                               ("Error: unable to create OpenSSL subprocess!"));
992     m_fclose(&fperr);
993     m_fclose(&fpout);
994     mutt_unlink (pk7out);
995     mutt_unlink (certfile);
996     return NULL;
997   }
998
999   mutt_wait_filter (thepid);
1000
1001   fflush (fpout);
1002   rewind (fpout);
1003   rewind (fperr);
1004   fflush (fperr);
1005   empty = (fgetc (fpout) == EOF);
1006   m_fclose(&fpout);
1007
1008   if (empty) {
1009     mutt_endwin (NULL);
1010     mutt_copy_stream (fperr, stdout);
1011     mutt_any_key_to_continue (NULL);
1012     m_fclose(&fperr);
1013     mutt_unlink (certfile);
1014     return NULL;
1015   }
1016
1017   m_fclose(&fperr);
1018
1019   return m_strdup(certfile);
1020 }
1021
1022 /* Add a certificate and update index file (externally). */
1023
1024 void smime_invoke_import (char *infile, char *mailbox __attribute__ ((unused)))
1025 {
1026   char tmpfname[_POSIX_PATH_MAX], *certfile = NULL, buf[STRING];
1027   FILE *smimein = NULL, *fpout = NULL, *fperr = NULL;
1028   pid_t thepid = -1;
1029
1030  fperr = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1031   if (!fperr) {
1032     mutt_perror (tmpfname);
1033     return;
1034   }
1035   mutt_unlink (tmpfname);
1036
1037   fpout = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1038   if (!fpout) {
1039     m_fclose(&fperr);
1040     mutt_perror (tmpfname);
1041     return;
1042   }
1043   mutt_unlink (tmpfname);
1044
1045   buf[0] = '\0';
1046   if (option (OPTASKCERTLABEL))
1047     mutt_get_field ("Label for certificate:", buf, sizeof (buf), 0);
1048
1049   mutt_endwin (NULL);
1050   if ((certfile = smime_extract_certificate (infile))) {
1051     mutt_endwin (NULL);
1052
1053     if ((thepid = smime_invoke (&smimein, NULL, NULL,
1054                                 -1, fileno (fpout), fileno (fperr),
1055                                 certfile, NULL, NULL, NULL, NULL, NULL,
1056                                 SmimeImportCertCommand)) == -1) {
1057       mutt_message (_("Error: unable to create OpenSSL subprocess!"));
1058       return;
1059     }
1060     fputs (buf, smimein);
1061     fputc ('\n', smimein);
1062     m_fclose(&smimein);
1063
1064     mutt_wait_filter (thepid);
1065
1066     mutt_unlink (certfile);
1067     p_delete(&certfile);
1068   }
1069
1070   fflush (fpout);
1071   rewind (fpout);
1072   fflush (fperr);
1073   rewind (fperr);
1074
1075   mutt_copy_stream (fpout, stdout);
1076   mutt_copy_stream (fperr, stdout);
1077
1078   m_fclose(&fpout);
1079   m_fclose(&fperr);
1080 }
1081
1082 int smime_verify_sender (HEADER * h)
1083 {
1084   char *mbox = NULL, *certfile, tempfname[_POSIX_PATH_MAX];
1085   FILE *fpout;
1086   int retval = 1;
1087
1088   fpout = m_tempfile (tempfname, sizeof(tempfname), NONULL(Tempdir), NULL);
1089   if (!fpout) {
1090     mutt_perror (_("Can't create temporary file"));
1091     return 1;
1092   }
1093
1094   if (h->security & ENCRYPT)
1095     mutt_copy_message (fpout, Context, h,
1096                        M_CM_DECODE_CRYPT & M_CM_DECODE_SMIME,
1097                        CH_MIME | CH_WEED | CH_NONEWLINE);
1098   else
1099     mutt_copy_message (fpout, Context, h, 0, 0);
1100
1101   fflush (fpout);
1102   m_fclose(&fpout);
1103
1104   if (h->env->from) {
1105     h->env->from = mutt_expand_aliases (h->env->from);
1106     mbox = h->env->from->mailbox;
1107   }
1108   else if (h->env->sender) {
1109     h->env->sender = mutt_expand_aliases (h->env->sender);
1110     mbox = h->env->sender->mailbox;
1111   }
1112
1113   if (mbox) {
1114     if ((certfile = smime_extract_signer_certificate (tempfname))) {
1115       mutt_unlink (tempfname);
1116       if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL)) {
1117         if (isendwin ())
1118           mutt_any_key_to_continue (NULL);
1119       }
1120       else
1121         retval = 0;
1122       mutt_unlink (certfile);
1123       p_delete(&certfile);
1124     }
1125     else
1126       mutt_any_key_to_continue (_("no certfile"));
1127   }
1128   else
1129     mutt_any_key_to_continue (_("no mbox"));
1130
1131   mutt_unlink (tempfname);
1132   return retval;
1133 }
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143 /*
1144  *    Creating S/MIME - bodies.
1145  */
1146
1147
1148
1149
1150 static
1151 pid_t smime_invoke_encrypt (FILE ** smimein, FILE ** smimeout,
1152                             FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1153                             int smimeerrfd, const char *fname,
1154                             const char *uids)
1155 {
1156   return smime_invoke (smimein, smimeout, smimeerr,
1157                        smimeinfd, smimeoutfd, smimeerrfd,
1158                        fname, NULL, SmimeCryptAlg, NULL, uids, NULL,
1159                        SmimeEncryptCommand);
1160 }
1161
1162
1163 static
1164 pid_t smime_invoke_sign (FILE ** smimein, FILE ** smimeout, FILE ** smimeerr,
1165                          int smimeinfd, int smimeoutfd, int smimeerrfd,
1166                          const char *fname)
1167 {
1168   return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1169                        smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1170                        SmimeCertToUse, SmimeIntermediateToUse,
1171                        SmimeSignCommand);
1172 }
1173
1174
1175
1176
1177 BODY *smime_build_smime_entity (BODY * a, char *certlist)
1178 {
1179   char buf[LONG_STRING], certfile[LONG_STRING];
1180   char tempfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1181   char smimeinfile[_POSIX_PATH_MAX];
1182   char *cert_start = certlist, *cert_end = certlist;
1183   FILE *smimein = NULL, *smimeerr = NULL, *fpout = NULL, *fptmp = NULL;
1184   BODY *t;
1185   int err = 0, empty;
1186   pid_t thepid;
1187
1188   fpout = m_tempfile (tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
1189   if (!fpout) {
1190     mutt_perror (_("Can't create temporary file"));
1191     return NULL;
1192   }
1193
1194   smimeerr = m_tempfile (smimeerrfile, sizeof(smimeerrfile), NONULL(Tempdir), NULL);
1195   if (!smimeerr) {
1196     mutt_perror (smimeerrfile);
1197     m_fclose(&fpout);
1198     mutt_unlink (tempfile);
1199     return NULL;
1200   }
1201   mutt_unlink (smimeerrfile);
1202
1203   fptmp = m_tempfile (smimeinfile, sizeof(smimeinfile), NONULL(Tempdir), NULL);
1204   if (!fptmp) {
1205     mutt_perror (smimeinfile);
1206     mutt_unlink (tempfile);
1207     m_fclose(&fpout);
1208     m_fclose(&smimeerr);
1209     return NULL;
1210   }
1211
1212   *certfile = '\0';
1213   while (1) {
1214     int off = m_strlen(certfile);
1215
1216     while (*++cert_end && *cert_end != '\n');
1217     if (!*cert_end)
1218       break;
1219     *cert_end = '\0';
1220     snprintf (certfile + off, sizeof (certfile) - off, " %s/%s",
1221               NONULL (SmimeCertificates), cert_start);
1222     *cert_end = '\n';
1223     cert_start = cert_end;
1224     cert_start++;
1225   }
1226
1227   /* write a MIME entity */
1228   mutt_write_mime_header (a, fptmp);
1229   fputc ('\n', fptmp);
1230   mutt_write_mime_body (a, fptmp);
1231   m_fclose(&fptmp);
1232
1233   if ((thepid =
1234        smime_invoke_encrypt (&smimein, NULL, NULL, -1,
1235                              fileno (fpout), fileno (smimeerr),
1236                              smimeinfile, certfile)) == -1) {
1237     m_fclose(&smimeerr);
1238     mutt_unlink (smimeinfile);
1239     mutt_unlink (certfile);
1240     return (NULL);
1241   }
1242
1243   m_fclose(&smimein);
1244
1245   mutt_wait_filter (thepid);
1246   mutt_unlink (smimeinfile);
1247   mutt_unlink (certfile);
1248
1249   fflush (fpout);
1250   rewind (fpout);
1251   empty = (fgetc (fpout) == EOF);
1252   m_fclose(&fpout);
1253
1254   fflush (smimeerr);
1255   rewind (smimeerr);
1256   while (fgets (buf, sizeof (buf) - 1, smimeerr) != NULL) {
1257     err = 1;
1258     fputs (buf, stdout);
1259   }
1260   m_fclose(&smimeerr);
1261
1262   /* pause if there is any error output from SMIME */
1263   if (err)
1264     mutt_any_key_to_continue (NULL);
1265
1266   if (empty) {
1267     /* fatal error while trying to encrypt message */
1268     if (!err)
1269       mutt_any_key_to_continue _("No output from OpenSSL..");
1270
1271     mutt_unlink (tempfile);
1272     return (NULL);
1273   }
1274
1275   t = body_new();
1276   t->type = TYPEAPPLICATION;
1277   t->subtype = m_strdup("x-pkcs7-mime");
1278   parameter_setval(&t->parameter, "name", "smime.p7m");
1279   parameter_setval(&t->parameter, "smime-type", "enveloped-data");
1280   t->encoding = ENCBASE64;      /* The output of OpenSSL SHOULD be binary */
1281   t->use_disp = 1;
1282   t->disposition = DISPATTACH;
1283   t->d_filename = m_strdup("smime.p7m");
1284   t->filename = m_strdup(tempfile);
1285   t->unlink = 1;                /*delete after sending the message */
1286   t->parts = 0;
1287   t->next = 0;
1288
1289   return (t);
1290 }
1291
1292
1293
1294
1295 BODY *smime_sign_message (BODY * a)
1296 {
1297   BODY *t;
1298   char buffer[LONG_STRING];
1299   char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX];
1300   FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL;
1301   int err = 0;
1302   int empty = 0;
1303   pid_t thepid;
1304   char *intermediates = smime_get_field_from_db (NULL, SmimeDefaultKey, 1, 1);
1305
1306   if (!intermediates) {
1307     mutt_message (_("Warning: Intermediate certificate not found."));
1308     intermediates = SmimeDefaultKey;    /* so openssl won't complain in any case */
1309   }
1310
1311   convert_to_7bit (a);          /* Signed data _must_ be in 7-bit format. */
1312
1313   sfp = m_tempfile (filetosign, sizeof(filetosign), NONULL(Tempdir), NULL);
1314   if (!sfp) {
1315     mutt_perror (filetosign);
1316     return NULL;
1317   }
1318
1319   smimeout = m_tempfile (signedfile, sizeof(signedfile), NONULL(Tempdir), NULL);
1320   if (!smimeout) {
1321     mutt_perror (signedfile);
1322     m_fclose(&sfp);
1323     mutt_unlink (filetosign);
1324     return NULL;
1325   }
1326
1327   mutt_write_mime_header (a, sfp);
1328   fputc ('\n', sfp);
1329   mutt_write_mime_body (a, sfp);
1330   m_fclose(&sfp);
1331
1332
1333
1334   snprintf (SmimeKeyToUse, sizeof (SmimeKeyToUse), "%s/%s",
1335             NONULL (SmimeKeys), SmimeDefaultKey);
1336
1337   snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
1338             NONULL (SmimeCertificates), SmimeDefaultKey);
1339
1340   snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
1341             NONULL (SmimeCertificates), intermediates);
1342
1343
1344
1345   if ((thepid = smime_invoke_sign (&smimein, NULL, &smimeerr,
1346                                    -1, fileno (smimeout), -1,
1347                                    filetosign)) == -1) {
1348     mutt_perror (_("Can't open OpenSSL subprocess!"));
1349
1350     m_fclose(&smimeout);
1351     mutt_unlink (signedfile);
1352     mutt_unlink (filetosign);
1353     return NULL;
1354   }
1355   fputs (SmimePass, smimein);
1356   fputc ('\n', smimein);
1357   m_fclose(&smimein);
1358
1359
1360   mutt_wait_filter (thepid);
1361
1362   /* check for errors from OpenSSL */
1363   err = 0;
1364   fflush (smimeerr);
1365   rewind (smimeerr);
1366   while (fgets (buffer, sizeof (buffer) - 1, smimeerr) != NULL) {
1367     err = 1;
1368     fputs (buffer, stdout);
1369   }
1370   m_fclose(&smimeerr);
1371
1372
1373   fflush (smimeout);
1374   rewind (smimeout);
1375   empty = (fgetc (smimeout) == EOF);
1376   m_fclose(&smimeout);
1377
1378   mutt_unlink (filetosign);
1379
1380
1381   if (err)
1382     mutt_any_key_to_continue (NULL);
1383
1384   if (empty) {
1385     mutt_any_key_to_continue _("No output from OpenSSL...");
1386
1387     mutt_unlink (signedfile);
1388     return (NULL);              /* fatal error while signing */
1389   }
1390
1391   t = body_new();
1392   t->type = TYPEMULTIPART;
1393   t->subtype = m_strdup("signed");
1394   t->encoding = ENC7BIT;
1395   t->use_disp = 0;
1396   t->disposition = DISPINLINE;
1397
1398   parameter_set_boundary(&t->parameter);
1399   /* check if this can be extracted from private key somehow.... */
1400   parameter_setval(&t->parameter, "micalg", "sha1");
1401   parameter_setval(&t->parameter, "protocol",
1402                    "application/x-pkcs7-signature");
1403
1404   t->parts = a;
1405   a = t;
1406
1407   t->parts->next = body_new();
1408   t = t->parts->next;
1409   t->type = TYPEAPPLICATION;
1410   t->subtype = m_strdup("x-pkcs7-signature");
1411   t->filename = m_strdup(signedfile);
1412   t->d_filename = m_strdup("smime.p7s");
1413   t->use_disp = 1;
1414   t->disposition = DISPATTACH;
1415   t->encoding = ENCBASE64;
1416   t->unlink = 1;                /* ok to remove this file after sending. */
1417
1418   return (a);
1419
1420 }
1421
1422
1423 /*
1424  *    Handling S/MIME - bodies.
1425  */
1426
1427
1428 static
1429 pid_t smime_invoke_verify (FILE ** smimein, FILE ** smimeout,
1430                            FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1431                            int smimeerrfd, const char *fname,
1432                            const char *sig_fname, int opaque)
1433 {
1434   return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1435                        smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL,
1436                        (opaque ? SmimeVerifyOpaqueCommand :
1437                         SmimeVerifyCommand));
1438 }
1439
1440
1441 static
1442 pid_t smime_invoke_decrypt (FILE ** smimein, FILE ** smimeout,
1443                             FILE ** smimeerr, int smimeinfd, int smimeoutfd,
1444                             int smimeerrfd, const char *fname)
1445 {
1446   return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd,
1447                        smimeerrfd, fname, NULL, NULL, SmimeKeyToUse,
1448                        SmimeCertToUse, NULL, SmimeDecryptCommand);
1449 }
1450
1451
1452
1453 int smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1454 {
1455   char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
1456   FILE *fp = NULL, *smimeout = NULL, *smimeerr = NULL;
1457   pid_t thepid;
1458   int badsig = -1;
1459
1460   long tmpoffset = 0;
1461   ssize_t tmplength = 0;
1462   int origType = sigbdy->type;
1463   char *savePrefix = NULL;
1464
1465
1466   snprintf (signedfile, sizeof (signedfile), "%s.sig", tempfile);
1467
1468   /* decode to a tempfile, saving the original destination */
1469   fp = s->fpout;
1470   if ((s->fpout = safe_fopen (signedfile, "w")) == NULL) {
1471     mutt_perror (signedfile);
1472     return -1;
1473   }
1474   /* decoding the attachment changes the size and offset, so save a copy
1475    * of the "real" values now, and restore them after processing
1476    */
1477   tmplength = sigbdy->length;
1478   tmpoffset = sigbdy->offset;
1479
1480   /* if we are decoding binary bodies, we don't want to prefix each
1481    * line with the prefix or else the data will get corrupted.
1482    */
1483   savePrefix = s->prefix;
1484   s->prefix = NULL;
1485
1486   mutt_decode_attachment (sigbdy, s);
1487
1488   sigbdy->length = ftello (s->fpout);
1489   sigbdy->offset = 0;
1490   m_fclose(&s->fpout);
1491
1492   /* restore final destination and substitute the tempfile for input */
1493   s->fpout = fp;
1494   fp = s->fpin;
1495   s->fpin = fopen (signedfile, "r");
1496
1497   /* restore the prefix */
1498   s->prefix = savePrefix;
1499
1500   sigbdy->type = origType;
1501
1502   smimeerr = m_tempfile(smimeerrfile, sizeof(smimeerrfile), NONULL(Tempdir), NULL);
1503   if (!smimeerr) {
1504     mutt_perror (smimeerrfile);
1505     mutt_unlink (signedfile);
1506     return -1;
1507   }
1508
1509   crypt_current_time (s, "OpenSSL");
1510
1511   if ((thepid = smime_invoke_verify (NULL, &smimeout, NULL,
1512                                      -1, -1, fileno (smimeerr),
1513                                      tempfile, signedfile, 0)) != -1) {
1514     m_fclose(&smimeout);
1515
1516     if (mutt_wait_filter (thepid))
1517       badsig = -1;
1518     else {
1519       char *line = NULL;
1520       int lineno = 0;
1521       ssize_t linelen;
1522
1523       fflush (smimeerr);
1524       rewind (smimeerr);
1525
1526       line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1527       if (linelen && !m_strcasecmp(line, "verification successful"))
1528         badsig = 0;
1529
1530       p_delete(&line);
1531     }
1532   }
1533
1534   fflush (smimeerr);
1535   rewind (smimeerr);
1536   mutt_copy_stream (smimeerr, s->fpout);
1537   m_fclose(&smimeerr);
1538
1539   state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1540
1541   mutt_unlink (signedfile);
1542   mutt_unlink (smimeerrfile);
1543
1544   sigbdy->length = tmplength;
1545   sigbdy->offset = tmpoffset;
1546
1547   /* restore the original source stream */
1548   m_fclose(&s->fpin);
1549   s->fpin = fp;
1550
1551
1552   return badsig;
1553 }
1554
1555
1556
1557
1558
1559 /*
1560   This handles application/pkcs7-mime which can either be a signed
1561   or an encrypted message.
1562 */
1563
1564 static BODY *smime_handle_entity (BODY * m, STATE * s, FILE * outFile)
1565 {
1566   int len = 0;
1567   int c;
1568   long last_pos;
1569   char buf[HUGE_STRING];
1570   char outfile[_POSIX_PATH_MAX], errfile[_POSIX_PATH_MAX];
1571   char tmpfname[_POSIX_PATH_MAX];
1572   char tmptmpfname[_POSIX_PATH_MAX];
1573   FILE *smimeout = NULL, *smimein = NULL, *smimeerr = NULL;
1574   FILE *tmpfp = NULL, *tmpfp_buffer = NULL, *fpout = NULL;
1575   struct stat info;
1576   BODY *p = NULL;
1577   pid_t thepid = -1;
1578   unsigned int type = mutt_is_application_smime (m);
1579
1580   if (!(type & APPLICATION_SMIME))
1581     return NULL;
1582
1583   smimeout = m_tempfile (outfile, sizeof(outfile), NONULL(Tempdir), NULL);
1584   if (!smimeout) {
1585     mutt_perror (outfile);
1586     return NULL;
1587   }
1588
1589   smimeerr = m_tempfile(errfile, sizeof(errfile), NONULL(Tempdir), NULL);
1590   if (!smimeerr) {
1591     mutt_perror (errfile);
1592     m_fclose(&smimeout);
1593     return NULL;
1594   }
1595   mutt_unlink (errfile);
1596
1597   tmpfp = m_tempfile (tmpfname, sizeof(tmpfname), NONULL(Tempdir), NULL);
1598   if (!tmpfp) {
1599     mutt_perror (tmpfname);
1600     m_fclose(&smimeout);
1601     m_fclose(&smimeerr);
1602     return NULL;
1603   }
1604
1605   fseeko (s->fpin, m->offset, 0);
1606   last_pos = m->offset;
1607
1608   mutt_copy_bytes (s->fpin, tmpfp, m->length);
1609   m_fclose(&tmpfp);
1610
1611   if ((type & ENCRYPT) &&
1612       (thepid = smime_invoke_decrypt (&smimein, NULL, NULL, -1,
1613                                       fileno (smimeout), fileno (smimeerr),
1614                                       tmpfname)) == -1) {
1615     m_fclose(&smimeout);
1616     mutt_unlink (tmpfname);
1617     if (s->flags & M_DISPLAY)
1618       state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1619     return NULL;
1620   }
1621   else if ((type & SIGNOPAQUE) &&
1622            (thepid = smime_invoke_verify (&smimein, NULL, NULL, -1,
1623                                           fileno (smimeout),
1624                                           fileno (smimeerr), NULL, tmpfname,
1625                                           SIGNOPAQUE)) == -1) {
1626     m_fclose(&smimeout);
1627     mutt_unlink (tmpfname);
1628     if (s->flags & M_DISPLAY)
1629       state_attach_puts (_("[-- Error: unable to create OpenSSL subprocess! --]\n"), s);
1630     return NULL;
1631   }
1632
1633
1634   if (type & ENCRYPT) {
1635     if (!smime_valid_passphrase ())
1636       smime_void_passphrase ();
1637     fputs (SmimePass, smimein);
1638     fputc ('\n', smimein);
1639   }
1640
1641   m_fclose(&smimein);
1642
1643   mutt_wait_filter (thepid);
1644   mutt_unlink (tmpfname);
1645
1646
1647   if (s->flags & M_DISPLAY) {
1648     rewind (smimeerr);
1649
1650     if ((c = fgetc (smimeerr)) != EOF) {
1651       ungetc (c, smimeerr);
1652
1653       crypt_current_time (s, "OpenSSL");
1654       mutt_copy_stream (smimeerr, s->fpout);
1655       state_attach_puts (_("[-- End of OpenSSL output --]\n\n"), s);
1656     }
1657
1658     if (type & ENCRYPT)
1659       state_attach_puts (_("[-- The following data is S/MIME"
1660                            " encrypted --]\n"), s);
1661     else
1662       state_attach_puts (_("[-- The following data is S/MIME signed --]\n"),
1663                          s);
1664   }
1665
1666   if (smimeout) {
1667     fflush (smimeout);
1668     rewind (smimeout);
1669
1670     if (outFile)
1671       fpout = outFile;
1672     else {
1673       fpout = m_tempfile (tmptmpfname, sizeof(tmptmpfname), NONULL(Tempdir), NULL);
1674       if (!fpout) {
1675         mutt_perror (tmptmpfname);
1676         m_fclose(&smimeout);
1677         return NULL;
1678       }
1679     }
1680     while (fgets (buf, sizeof (buf) - 1, smimeout) != NULL) {
1681       len = m_strlen(buf);
1682       if (len > 1 && buf[len - 2] == '\r') {
1683         buf[len - 2] = '\n';
1684         buf[len - 1] = '\0';
1685       }
1686       fputs (buf, fpout);
1687     }
1688     fflush (fpout);
1689     rewind (fpout);
1690
1691
1692     if ((p = mutt_read_mime_header (fpout, 0)) != NULL) {
1693       fstat (fileno (fpout), &info);
1694       p->length = info.st_size - p->offset;
1695
1696       mutt_parse_part (fpout, p);
1697       if (s->fpout) {
1698         rewind (fpout);
1699         tmpfp_buffer = s->fpin;
1700         s->fpin = fpout;
1701         mutt_body_handler (p, s);
1702         s->fpin = tmpfp_buffer;
1703       }
1704
1705     }
1706     m_fclose(&smimeout);
1707     mutt_unlink (outfile);
1708
1709     if (!outFile) {
1710       m_fclose(&fpout);
1711       mutt_unlink (tmptmpfname);
1712     }
1713     fpout = NULL;
1714   }
1715
1716   if (s->flags & M_DISPLAY) {
1717     if (type & ENCRYPT)
1718       state_attach_puts (_("\n[-- End of S/MIME encrypted data. --]\n"), s);
1719     else
1720       state_attach_puts (_("\n[-- End of S/MIME signed data. --]\n"), s);
1721   }
1722
1723   if (type & SIGNOPAQUE) {
1724     char *line = NULL;
1725     int lineno = 0;
1726     ssize_t linelen;
1727
1728     rewind (smimeerr);
1729
1730     line = mutt_read_line (line, &linelen, smimeerr, &lineno);
1731     if (linelen && !m_strcasecmp(line, "verification successful"))
1732       m->goodsig = 1;
1733     p_delete(&line);
1734   }
1735   else {
1736     m->goodsig = p->goodsig;
1737     m->badsig = p->badsig;
1738   }
1739   m_fclose(&smimeerr);
1740
1741   return (p);
1742 }
1743
1744
1745
1746
1747
1748 int smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1749 {
1750
1751
1752   char tempfile[_POSIX_PATH_MAX];
1753   STATE s;
1754   long tmpoffset = b->offset;
1755   ssize_t tmplength = b->length;
1756   int origType = b->type;
1757   FILE *tmpfp = NULL;
1758   int rv = 0;
1759
1760   if (!mutt_is_application_smime (b))
1761     return -1;
1762
1763   if (b->parts)
1764     return -1;
1765
1766   p_clear(&s, 1);
1767   s.fpin = fpin;
1768   fseeko (s.fpin, b->offset, 0);
1769
1770   tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
1771   if (!tmpfp) {
1772     mutt_perror (_("Can't create temporary file"));
1773     return (-1);
1774   }
1775
1776   mutt_unlink (tempfile);
1777   s.fpout = tmpfp;
1778   mutt_decode_attachment (b, &s);
1779   fflush (tmpfp);
1780   b->length = ftello (s.fpout);
1781   b->offset = 0;
1782   rewind (tmpfp);
1783   s.fpin = tmpfp;
1784   s.fpout = 0;
1785
1786   *fpout = m_tempfile (tempfile, sizeof(tempfile), NONULL(Tempdir), NULL);
1787   if (!*fpout) {
1788     mutt_perror (_("Can't create temporary file"));
1789     rv = -1;
1790     goto bail;
1791   }
1792   mutt_unlink (tempfile);
1793
1794   if (!(*cur = smime_handle_entity (b, &s, *fpout))) {
1795     rv = -1;
1796     goto bail;
1797   }
1798
1799   (*cur)->goodsig = b->goodsig;
1800   (*cur)->badsig  = b->badsig;
1801
1802 bail:
1803   b->type = origType;
1804   b->length = tmplength;
1805   b->offset = tmpoffset;
1806
1807   m_fclose(&tmpfp);
1808   if (*fpout)
1809     rewind (*fpout);
1810   return (rv);
1811 }
1812
1813
1814 int smime_application_smime_handler (BODY * m, STATE * s)
1815 {
1816   return smime_handle_entity (m, s, NULL) ? 0 : -1;
1817 }
1818
1819 int smime_send_menu (HEADER * msg, int *redraw)
1820 {
1821   char *p;
1822
1823   switch (mutt_multi_choice
1824           (_("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear? "),
1825            _("eswabfc"))) {
1826   case 1:                      /* (e)ncrypt */
1827     msg->security |= ENCRYPT;
1828     msg->security &= ~SIGN;
1829     break;
1830
1831   case 3:                      /* encrypt (w)ith */
1832     {
1833       int choice = 0;
1834       msg->security |= ENCRYPT;
1835
1836       do {
1837         /* I use "dra" because "123" is recognized anyway */
1838         switch (mutt_multi_choice (_("Choose algorithm family:"
1839                                      " 1: DES, 2: RC2, 3: AES,"
1840                                      " or (c)lear? "), _("drac"))) {
1841           case 1:
1842             switch (choice = mutt_multi_choice (_("1: DES, 2: Triple-DES "),
1843                                                 _("dt"))) {
1844               case 1:
1845                 m_strreplace(&SmimeCryptAlg, "des");
1846                 break;
1847               case 2:
1848                 m_strreplace(&SmimeCryptAlg, "des3");
1849                 break;
1850             }
1851             break;
1852
1853           case 2:
1854             switch (choice = mutt_multi_choice (_("1: RC2-40, 2: RC2-64, 3: RC2-128 "),
1855                                                 _("468"))) {
1856               case 1:
1857                 m_strreplace(&SmimeCryptAlg, "rc2-40");
1858                 break;
1859               case 2:
1860                 m_strreplace(&SmimeCryptAlg, "rc2-64");
1861                 break;
1862               case 3:
1863                 m_strreplace(&SmimeCryptAlg, "rc2-128");
1864                 break;
1865             }
1866             break;
1867
1868           case 3:
1869             switch (choice = mutt_multi_choice (_("1: AES128, 2: AES192, 3: AES256 "),
1870                                                 _("895"))) {
1871               case 1:
1872                 m_strreplace(&SmimeCryptAlg, "aes128");
1873                 break;
1874               case 2:
1875                 m_strreplace(&SmimeCryptAlg, "aes192");
1876                 break;
1877               case 3:
1878                 m_strreplace(&SmimeCryptAlg, "aes256");
1879                 break;
1880             }
1881             break;
1882
1883           case 4: /* (c)lear */
1884             p_delete(&SmimeCryptAlg);
1885             /* fallback */
1886           case -1: /* Ctrl-G or Enter */
1887             choice = 0;
1888             break;
1889         }
1890       } while (choice == -1);
1891     }
1892     break;
1893
1894   case 2:                      /* (s)ign */
1895
1896     if (!SmimeDefaultKey)
1897       mutt_message (_("Can't sign: No key specified. Use Sign As."));
1898
1899     else {
1900       msg->security |= SIGN;
1901       msg->security &= ~ENCRYPT;
1902     }
1903     break;
1904
1905   case 4:                      /* sign (a)s */
1906
1907     if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) {
1908       m_strreplace(&SmimeDefaultKey, p);
1909
1910       msg->security |= SIGN;
1911
1912       /* probably need a different passphrase */
1913       crypt_smime_void_passphrase ();
1914     }
1915
1916     *redraw = REDRAW_FULL;
1917     break;
1918
1919   case 5:                      /* (b)oth */
1920     msg->security |= (ENCRYPT | SIGN);
1921     break;
1922
1923   case 6:                      /* (f)orget it */
1924   case 7:                      /* (c)lear */
1925     msg->security = 0;
1926     break;
1927   }
1928
1929   if (msg->security && msg->security != APPLICATION_SMIME)
1930     msg->security |= APPLICATION_SMIME;
1931   else
1932     msg->security = 0;
1933
1934   return (msg->security);
1935 }