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