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