730c455081906fd6013ac96b1e0ccd711d7ae43e
[apps/madmutt.git] / lib-crypt / crypt.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996,1997 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
5  * Copyright (C) 2001  Thomas Roessler <roessler@does-not-exist.org>
6  *                     Oliver Ehli <elmy@acm.org>
7  * Copyright (C) 2003  Werner Koch <wk@gnupg.org>
8  * Copyright (C) 2004 g10code GmbH
9  *
10  * This file is part of mutt-ng, see http://www.muttng.org/.
11  * It's licensed under the GNU General Public License,
12  * please see the file GPL in the top level source directory.
13  */
14
15 #include <lib-lib/lib-lib.h>
16
17 #ifdef HAVE_LOCALE_H
18 #  include <locale.h>
19 #endif
20 #ifdef HAVE_SYS_RESOURCE_H
21 #  include <sys/resource.h>
22 #endif
23
24 #include <lib-mime/mime.h>
25
26 #include <lib-ui/curses.h>
27 #include <lib-mx/mx.h>
28
29 #include "alias.h"
30 #include "handler.h"
31 #include "copy.h"
32 #include "crypt.h"
33
34 /* print the current time to avoid spoofing of the signature output */
35 void crypt_current_time (STATE * s, const char *app_name)
36 {
37   time_t t;
38   char p[STRING], tmp[STRING];
39
40   if (option (OPTCRYPTTIMESTAMP)) {
41     t = time (NULL);
42     setlocale (LC_TIME, "");
43     strftime (p, sizeof (p), _(" (current time: %c)"), localtime (&t));
44     setlocale (LC_TIME, "C");
45   }
46   else
47     *p = '\0';
48
49   snprintf (tmp, sizeof (tmp), _("[-- %s output follows%s --]\n"),
50             NONULL (app_name), p);
51   state_attach_puts (tmp, s);
52 }
53
54 int mutt_protect (HEADER * msg, char *keylist)
55 {
56   BODY *pbody = NULL, *tmp_pbody = NULL;
57   BODY *tmp_smime_pbody = NULL;
58   BODY *tmp_pgp_pbody = NULL;
59   int flags = msg->security;
60
61   if (!isendwin ())
62     mutt_endwin (NULL);
63
64   tmp_smime_pbody = msg->content;
65   tmp_pgp_pbody = msg->content;
66
67   if (msg->security & SIGN) {
68     if (msg->security & APPLICATION_SMIME) {
69       if (!(tmp_pbody = crypt_smime_sign_message (msg->content)))
70         return -1;
71       pbody = tmp_smime_pbody = tmp_pbody;
72     }
73
74     if ((msg->security & APPLICATION_PGP)
75         && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
76       if (!(tmp_pbody = crypt_pgp_sign_message (msg->content)))
77         return -1;
78
79       flags &= ~SIGN;
80       pbody = tmp_pgp_pbody = tmp_pbody;
81     }
82
83     if ((msg->security & APPLICATION_SMIME)
84         && (msg->security & APPLICATION_PGP)) {
85       /* here comes the draft ;-) */
86     }
87   }
88
89
90   if (msg->security & ENCRYPT) {
91     if ((msg->security & APPLICATION_SMIME)) {
92       if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
93                                                         keylist))) {
94         /* signed ? free it! */
95         return (-1);
96       }
97       /* free tmp_body if messages was signed AND encrypted ... */
98       if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
99         /* detatch and dont't delete msg->content,
100            which tmp_smime_pbody->parts after signing. */
101         tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
102         msg->content->next = NULL;
103         body_list_wipe(&tmp_smime_pbody);
104       }
105       pbody = tmp_pbody;
106     }
107
108     if ((msg->security & APPLICATION_PGP)) {
109       if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
110                                                flags & SIGN))) {
111
112         /* did we perform a retainable signature? */
113         if (flags != msg->security) {
114           /* remove the outer multipart layer */
115           tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
116           /* get rid of the signature */
117           body_list_wipe(&tmp_pgp_pbody->next);
118         }
119
120         return (-1);
121       }
122
123       /* destroy temporary signature envelope when doing retainable 
124        * signatures.
125
126        */
127       if (flags != msg->security) {
128         tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
129         body_list_wipe(&tmp_pgp_pbody->next);
130       }
131     }
132   }
133
134   if (pbody)
135     msg->content = pbody;
136
137   return 0;
138 }
139
140
141 int crypt_query (BODY * m)
142 {
143   int t = 0;
144
145   if (!m)
146     return 0;
147
148   if (m->type == TYPEAPPLICATION) {
149     t |= mutt_is_application_pgp (m);
150
151     t |= mutt_is_application_smime (m);
152     if (t && m->goodsig)
153       t |= GOODSIGN;
154     if (t && m->badsig)
155       t |= BADSIGN;
156   }
157   else if (m->type == TYPETEXT) {
158     t |= mutt_is_application_pgp (m);
159     if (t && m->goodsig)
160       t |= GOODSIGN;
161   }
162
163   if (m->type == TYPEMULTIPART) {
164     t |= mutt_is_multipart_encrypted (m);
165     t |= mutt_is_multipart_signed (m);
166
167     if (t && m->goodsig)
168       t |= GOODSIGN;
169   }
170
171   if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
172     BODY *p;
173     int u, v, w;
174
175     u = m->parts ? 0xffffffff : 0;      /* Bits set in all parts */
176     w = 0;                      /* Bits set in any part  */
177
178     for (p = m->parts; p; p = p->next) {
179       v = crypt_query (p);
180       u &= v;
181       w |= v;
182     }
183     t |= u | (w & ~GOODSIGN);
184
185     if ((w & GOODSIGN) && !(u & GOODSIGN))
186       t |= PARTSIGN;
187   }
188
189   return t;
190 }
191
192
193 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
194 {
195     int c;
196     short hadcr;
197     size_t bytes;
198
199     fseeko (s->fpin, a->hdr_offset, 0);
200     bytes = a->length + a->offset - a->hdr_offset;
201     hadcr = 0;
202     while (bytes > 0) {
203         if ((c = fgetc (s->fpin)) == EOF)
204             break;
205
206         bytes--;
207
208         if (c == '\r')
209             hadcr = 1;
210         else {
211             if (c == '\n' && !hadcr)
212                 fputc ('\r', fp);
213
214             hadcr = 0;
215         }
216         fputc (c, fp);
217     }
218 }
219
220
221
222 void convert_to_7bit (BODY * a)
223 {
224   while (a) {
225     if (a->type == TYPEMULTIPART) {
226       if (a->encoding != ENC7BIT) {
227         a->encoding = ENC7BIT;
228         convert_to_7bit (a->parts);
229       } else {
230         convert_to_7bit (a->parts);
231       }
232     }
233     else if (a->type == TYPEMESSAGE &&
234              m_strcasecmp(a->subtype, "delivery-status")) {
235       if (a->encoding != ENC7BIT)
236         mutt_message_to_7bit (a, NULL);
237     }
238     else if (a->encoding == ENC8BIT)
239       a->encoding = ENCQUOTEDPRINTABLE;
240     else if (a->encoding == ENCBINARY)
241       a->encoding = ENCBASE64;
242     else if (a->content && a->encoding != ENCBASE64 &&
243              (a->content->from || a->content->space))
244       a->encoding = ENCQUOTEDPRINTABLE;
245     a = a->next;
246   }
247 }
248
249
250 void crypt_extract_keys_from_messages (HEADER * h)
251 {
252   int i;
253   char tempfname[_POSIX_PATH_MAX];
254   address_t *tmp = NULL;
255   FILE *fpout;
256
257   fpout = m_tempfile(tempfname, sizeof(tempfname), NONULL(MCore.tmpdir), NULL);
258   if (!fpout) {
259     mutt_error(_("Could not create temporary file"));
260     return;
261   }
262
263   set_option (OPTDONTHANDLEPGPKEYS);
264
265   if (!h) {
266     for (i = 0; i < Context->vcount; i++) {
267       if (Context->hdrs[Context->v2r[i]]->tagged) {
268         mutt_parse_mime_message (Context, Context->hdrs[Context->v2r[i]]);
269         if (Context->hdrs[Context->v2r[i]]->security & APPLICATION_PGP) {
270           mutt_copy_message (fpout, Context, Context->hdrs[Context->v2r[i]],
271                              M_CM_DECODE | M_CM_CHARCONV, 0);
272           fflush (fpout);
273
274           mutt_endwin (_("Trying to extract PGP keys...\n"));
275           crypt_pgp_invoke_import (tempfname);
276         }
277
278         if (Context->hdrs[Context->v2r[i]]->security & APPLICATION_SMIME) {
279           if (Context->hdrs[Context->v2r[i]]->security & ENCRYPT)
280             mutt_copy_message (fpout, Context, Context->hdrs[Context->v2r[i]],
281                                M_CM_NOHEADER | M_CM_DECODE_CRYPT
282                                | M_CM_DECODE_SMIME, 0);
283           else
284             mutt_copy_message (fpout, Context,
285                                Context->hdrs[Context->v2r[i]], 0, 0);
286           fflush (fpout);
287
288           if (Context->hdrs[Context->v2r[i]]->env->from)
289             tmp = mutt_expand_aliases (h->env->from);
290           else if (Context->hdrs[Context->v2r[i]]->env->sender)
291             tmp = mutt_expand_aliases (Context->hdrs[Context->v2r[i]]
292                                        ->env->sender);
293           mutt_endwin (_("Trying to extract S/MIME certificates...\n"));
294           crypt_smime_invoke_import(tempfname);
295           tmp = NULL;
296         }
297
298         rewind (fpout);
299       }
300     }
301   }
302   else {
303     mutt_parse_mime_message (Context, h);
304     if (h->security & APPLICATION_PGP) {
305       mutt_copy_message (fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
306       fflush (fpout);
307       mutt_endwin (_("Trying to extract PGP keys...\n"));
308       crypt_pgp_invoke_import (tempfname);
309     }
310
311     if (h->security & APPLICATION_SMIME) {
312       if (h->security & ENCRYPT)
313         mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
314                            | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
315       else
316         mutt_copy_message (fpout, Context, h, 0, 0);
317
318       fflush (fpout);
319       if (h->env->from)
320         tmp = mutt_expand_aliases (h->env->from);
321       else if (h->env->sender)
322         tmp = mutt_expand_aliases (h->env->sender);
323       mutt_message (_("Trying to extract S/MIME certificates...\n"));
324       crypt_smime_invoke_import(tempfname);
325     }
326   }
327
328   m_fclose(&fpout);
329   if (isendwin ())
330     mutt_any_key_to_continue (NULL);
331
332   mutt_unlink (tempfname);
333
334   unset_option (OPTDONTHANDLEPGPKEYS);
335 }
336
337
338
339 int crypt_get_keys (HEADER * msg, char **keylist)
340 {
341   /* Do a quick check to make sure that we can find all of the encryption
342    * keys if the user has requested this service.
343    */
344
345   set_option (OPTPGPCHECKTRUST);
346
347   *keylist = NULL;
348
349   if (msg->security & ENCRYPT) {
350     if (msg->security & APPLICATION_PGP) {
351       if ((*keylist = crypt_pgp_findkeys (msg->env->to, msg->env->cc,
352                                           msg->env->bcc)) == NULL)
353         return (-1);
354       unset_option (OPTPGPCHECKTRUST);
355     }
356     if (msg->security & APPLICATION_SMIME) {
357       if ((*keylist = crypt_smime_findkeys (msg->env->to, msg->env->cc,
358                                             msg->env->bcc)) == NULL)
359         return (-1);
360     }
361   }
362
363   return (0);
364 }
365
366
367
368 static void crypt_fetch_signatures (BODY ***signatures, BODY * a, int *n)
369 {
370   for (; a; a = a->next) {
371     if (a->type == TYPEMULTIPART)
372       crypt_fetch_signatures (signatures, a->parts, n);
373     else {
374       if ((*n % 5) == 0)
375         p_realloc(signatures, *n + 6);
376
377       (*signatures)[(*n)++] = a;
378     }
379   }
380 }
381
382
383 /*
384  * This routine verifies a  "multipart/signed"  body.
385  */
386
387 int mutt_signed_handler (BODY * a, STATE * s)
388 {
389   char tempfile[_POSIX_PATH_MAX];
390   FILE * tempfp;
391   char *protocol;
392   int protocol_major = TYPEOTHER;
393   char *protocol_minor = NULL;
394
395   BODY *b = a;
396   BODY **signatures = NULL;
397   int sigcnt = 0;
398   int i;
399   short goodsig = 1;
400   int rc = 0;
401
402   protocol = parameter_getval(a->parameter, "protocol");
403   a = a->parts;
404
405   /* extract the protocol information */
406
407   if (protocol) {
408     char major[STRING];
409     char *t;
410
411     if ((protocol_minor = strchr (protocol, '/')))
412       protocol_minor++;
413
414     m_strcpy(major, sizeof(major), protocol);
415     if ((t = strchr (major, '/')))
416       *t = '\0';
417
418     protocol_major = mutt_check_mime_type (major);
419   }
420
421   /* consistency check */
422
423   if (!(a && a->next && a->next->type == protocol_major &&
424         !m_strcasecmp(a->next->subtype, protocol_minor))) {
425     state_attach_puts (_("[-- Error: "
426                          "Inconsistent multipart/signed structure! --]\n\n"),
427                        s);
428     return mutt_body_handler (a, s);
429   }
430
431
432   if (protocol_major == TYPEAPPLICATION
433       && !m_strcasecmp(protocol_minor, "pgp-signature"));
434   else if (protocol_major == TYPEAPPLICATION
435            && !(m_strcasecmp(protocol_minor, "x-pkcs7-signature")
436                 && m_strcasecmp(protocol_minor, "pkcs7-signature")));
437   else if (protocol_major == TYPEMULTIPART
438            && !m_strcasecmp(protocol_minor, "mixed"));
439   else {
440     state_printf (s, _("[-- Error: "
441                        "Unknown multipart/signed protocol %s! --]\n\n"),
442                   protocol);
443     return mutt_body_handler (a, s);
444   }
445
446   if (s->flags & M_DISPLAY) {
447
448     crypt_fetch_signatures (&signatures, a->next, &sigcnt);
449
450     if (sigcnt) {
451       tempfp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
452       if (!tempfp) {
453           mutt_error(_("Could not create temporary file"));
454       } else {
455         crypt_write_signed(a, s, tempfp);
456         m_fclose(&tempfp);
457         for (i = 0; i < sigcnt; i++) {
458           if (signatures[i]->type == TYPEAPPLICATION
459               && !m_strcasecmp(signatures[i]->subtype, "pgp-signature")) {
460             if (crypt_pgp_verify_one (signatures[i], s, tempfile) != 0)
461               goodsig = 0;
462
463             continue;
464           }
465
466           if (signatures[i]->type == TYPEAPPLICATION
467           && (!m_strcasecmp(signatures[i]->subtype, "x-pkcs7-signature")
468                || !m_strcasecmp(signatures[i]->subtype, "pkcs7-signature")))
469           {
470             if (crypt_smime_verify_one (signatures[i], s, tempfile) != 0)
471               goodsig = 0;
472
473             continue;
474           }
475
476           state_printf (s, _("[-- Warning: "
477                              "We can't verify %s/%s signatures. --]\n\n"),
478                         TYPE (signatures[i]), signatures[i]->subtype);
479         }
480       }
481
482       mutt_unlink (tempfile);
483
484       b->goodsig = goodsig;
485       b->badsig = !goodsig;
486
487       /* Now display the signed body */
488       state_attach_puts (_("[-- The following data is signed --]\n\n"), s);
489
490
491       p_delete(&signatures);
492     }
493     else
494       state_attach_puts (_("[-- Warning: Can't find any signatures. --]\n\n"),
495                          s);
496   }
497
498   rc = mutt_body_handler (a, s);
499
500   if (s->flags & M_DISPLAY && sigcnt)
501     state_attach_puts (_("\n[-- End of signed data --]\n"), s);
502
503   return (rc);
504 }