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