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