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