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