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