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