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