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