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