60349cf0752bf2d77ad73ef1267df7620ba11eff
[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) 1998-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) 2002, 2003, 2004 g10 Code GmbH
9  */
10 /*
11  * Copyright © 2006 Pierre Habouzit
12  */
13
14 #include <lib-lib/lib-lib.h>
15
16 #include <lib-mime/mime.h>
17 #include <lib-ui/curses.h>
18 #include <lib-mx/mx.h>
19
20 #include "alias.h"
21 #include "handler.h"
22 #include "copy.h"
23 #include "crypt.h"
24
25 void crypt_invoke_message (int type)
26 {
27     if (type & APPLICATION_PGP) {
28         mutt_message _("Invoking PGP...");
29     }
30     else if (type & APPLICATION_SMIME) {
31         mutt_message _("Invoking S/MIME...");
32     }
33 }
34
35 int mutt_protect (HEADER * msg, char *keylist)
36 {
37   BODY *pbody = NULL, *tmp_pbody = NULL;
38   BODY *tmp_smime_pbody = NULL;
39   BODY *tmp_pgp_pbody = NULL;
40   int flags = msg->security;
41
42   if (!isendwin ())
43     mutt_endwin (NULL);
44
45   tmp_smime_pbody = msg->content;
46   tmp_pgp_pbody = msg->content;
47
48   if (msg->security & SIGN) {
49     if (msg->security & APPLICATION_SMIME) {
50       if (!(tmp_pbody = crypt_smime_sign_message (msg->content)))
51         return -1;
52       pbody = tmp_smime_pbody = tmp_pbody;
53     }
54
55     if ((msg->security & APPLICATION_PGP)
56         && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
57       if (!(tmp_pbody = crypt_pgp_sign_message (msg->content)))
58         return -1;
59
60       flags &= ~SIGN;
61       pbody = tmp_pgp_pbody = tmp_pbody;
62     }
63
64     if ((msg->security & APPLICATION_SMIME)
65         && (msg->security & APPLICATION_PGP)) {
66       /* here comes the draft ;-) */
67     }
68   }
69
70
71   if (msg->security & ENCRYPT) {
72     if ((msg->security & APPLICATION_SMIME)) {
73       if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
74                                                         keylist))) {
75         /* signed ? free it! */
76         return (-1);
77       }
78       /* free tmp_body if messages was signed AND encrypted ... */
79       if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
80         /* detatch and dont't delete msg->content,
81            which tmp_smime_pbody->parts after signing. */
82         tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
83         msg->content->next = NULL;
84         body_list_wipe(&tmp_smime_pbody);
85       }
86       pbody = tmp_pbody;
87     }
88
89     if ((msg->security & APPLICATION_PGP)) {
90       if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
91                                                flags & SIGN))) {
92
93         /* did we perform a retainable signature? */
94         if (flags != msg->security) {
95           /* remove the outer multipart layer */
96           tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
97           /* get rid of the signature */
98           body_list_wipe(&tmp_pgp_pbody->next);
99         }
100
101         return (-1);
102       }
103
104       /* destroy temporary signature envelope when doing retainable 
105        * signatures.
106
107        */
108       if (flags != msg->security) {
109         tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
110         body_list_wipe(&tmp_pgp_pbody->next);
111       }
112     }
113   }
114
115   if (pbody)
116     msg->content = pbody;
117
118   return 0;
119 }
120
121
122 int crypt_query (BODY * m)
123 {
124   int t = 0;
125
126   if (!m)
127     return 0;
128
129   if (m->type == TYPEAPPLICATION) {
130     t |= mutt_is_application_pgp (m);
131
132     t |= mutt_is_application_smime (m);
133     if (t && m->goodsig)
134       t |= GOODSIGN;
135     if (t && m->badsig)
136       t |= BADSIGN;
137   }
138   else if (m->type == TYPETEXT) {
139     t |= mutt_is_application_pgp (m);
140     if (t && m->goodsig)
141       t |= GOODSIGN;
142   }
143
144   if (m->type == TYPEMULTIPART) {
145     t |= mutt_is_multipart_encrypted (m);
146     t |= mutt_is_multipart_signed (m);
147
148     if (t && m->goodsig)
149       t |= GOODSIGN;
150   }
151
152   if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
153     BODY *p;
154     int u, v, w;
155
156     u = m->parts ? ~0 : 0;      /* Bits set in all parts */
157     w = 0;                      /* Bits set in any part  */
158
159     for (p = m->parts; p; p = p->next) {
160       v = crypt_query (p);
161       u &= v;
162       w |= v;
163     }
164     t |= u | (w & ~GOODSIGN);
165
166     if ((w & GOODSIGN) && !(u & GOODSIGN))
167       t |= PARTSIGN;
168   }
169
170   return t;
171 }
172
173
174 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
175 {
176     int c;
177     short hadcr;
178     size_t bytes;
179
180     fseeko (s->fpin, a->hdr_offset, 0);
181     bytes = a->length + a->offset - a->hdr_offset;
182     hadcr = 0;
183     while (bytes > 0) {
184         if ((c = fgetc (s->fpin)) == EOF)
185             break;
186
187         bytes--;
188
189         if (c == '\r')
190             hadcr = 1;
191         else {
192             if (c == '\n' && !hadcr)
193                 fputc ('\r', fp);
194
195             hadcr = 0;
196         }
197         fputc (c, fp);
198     }
199 }
200
201
202
203 void convert_to_7bit (BODY * a)
204 {
205   while (a) {
206     if (a->type == TYPEMULTIPART) {
207       if (a->encoding != ENC7BIT) {
208         a->encoding = ENC7BIT;
209         convert_to_7bit (a->parts);
210       } else {
211         convert_to_7bit (a->parts);
212       }
213     }
214     else if (a->type == TYPEMESSAGE &&
215              m_strcasecmp(a->subtype, "delivery-status")) {
216       if (a->encoding != ENC7BIT)
217         mutt_message_to_7bit (a, NULL);
218     }
219     else if (a->encoding == ENC8BIT)
220       a->encoding = ENCQUOTEDPRINTABLE;
221     else if (a->encoding == ENCBINARY)
222       a->encoding = ENCBASE64;
223     else if (a->content && a->encoding != ENCBASE64 &&
224              (a->content->from || a->content->space))
225       a->encoding = ENCQUOTEDPRINTABLE;
226     a = a->next;
227   }
228 }
229
230
231 static void extract_keys_aux(FILE *fpout, HEADER *h)
232 {
233     mutt_parse_mime_message (Context, h);
234
235     rewind(fpout);
236     if (h->security & APPLICATION_PGP) {
237         mutt_copy_message(fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
238         fflush (fpout);
239
240         mutt_endwin (_("Trying to extract PGP keys...\n"));
241     }
242
243     if (h->security & APPLICATION_SMIME) {
244         if (h->security & ENCRYPT)
245             mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
246                                | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
247         else
248             mutt_copy_message(fpout, Context, h, 0, 0);
249         fflush (fpout);
250
251         mutt_message (_("Trying to extract S/MIME certificates...\n"));
252     }
253
254     rewind(fpout);
255     crypt_invoke_import(fpout, h->security & APPLICATION_SMIME);
256 }
257
258 void crypt_extract_keys_from_messages(HEADER * h)
259 {
260     FILE *tmpfp = tmpfile();
261     if (!tmpfp) {
262         mutt_error(_("Could not create temporary file"));
263         return;
264     }
265
266     set_option(OPTDONTHANDLEPGPKEYS);
267     if (!h) {
268         int i;
269         for (i = 0; i < Context->vcount; i++) {
270             if (!Context->hdrs[Context->v2r[i]]->tagged)
271                 continue;
272             extract_keys_aux(tmpfp, Context->hdrs[Context->v2r[i]]);
273         }
274     } else {
275         extract_keys_aux(tmpfp, h);
276     }
277     unset_option(OPTDONTHANDLEPGPKEYS);
278     m_fclose(&tmpfp);
279
280     if (isendwin())
281         mutt_any_key_to_continue(NULL);
282 }
283
284
285
286 static void crypt_fetch_signatures (BODY ***signatures, BODY * a, int *n)
287 {
288   for (; a; a = a->next) {
289     if (a->type == TYPEMULTIPART)
290       crypt_fetch_signatures (signatures, a->parts, n);
291     else {
292       if ((*n % 5) == 0)
293         p_realloc(signatures, *n + 6);
294
295       (*signatures)[(*n)++] = a;
296     }
297   }
298 }
299
300
301 /*
302  * This routine verifies a  "multipart/signed"  body.
303  */
304
305 int mutt_signed_handler (BODY * a, STATE * s)
306 {
307   unsigned major, minor;
308   char *protocol;
309   int rc, i, goodsig = 1, sigcnt = 0;
310   BODY *b = a;
311
312   protocol = parameter_getval(a->parameter, "protocol");
313   a = a->parts;
314
315   switch (mime_which_token(protocol, -1)) {
316     case MIME_APPLICATION_PGP_SIGNATURE:
317       major = TYPEAPPLICATION;
318       minor = MIME_PGP_SIGNATURE;
319       break;
320     case MIME_APPLICATION_X_PKCS7_SIGNATURE:
321       major = TYPEAPPLICATION;
322       minor = MIME_X_PKCS7_SIGNATURE;
323       break;
324     case MIME_APPLICATION_PKCS7_SIGNATURE:
325       major = TYPEAPPLICATION;
326       minor = MIME_PKCS7_SIGNATURE;
327       break;
328     case MIME_MULTIPART_MIXED:
329       major = TYPEMULTIPART;
330       minor = MIME_MIXED;
331       break;
332
333     default:
334       state_printf(s, _("[-- Error: "
335                         "Unknown multipart/signed protocol %s! --]\n\n"),
336                     protocol);
337       return mutt_body_handler (a, s);
338   }
339
340   /* consistency check */
341   if (!(a && a->next && a->next->type == major &&
342         mime_which_token(a->next->subtype, -1) == minor))
343   {
344     state_attach_puts(_("[-- Error: "
345                         "Inconsistent multipart/signed structure! --]\n\n"),
346                       s);
347     return mutt_body_handler (a, s);
348   }
349
350   if (s->flags & M_DISPLAY) {
351     BODY **sigs = NULL;
352
353     crypt_fetch_signatures (&sigs, a->next, &sigcnt);
354     if (sigcnt) {
355       FILE *tmpfp = tmpfile();
356
357       if (!tmpfp) {
358           mutt_error(_("Could not create temporary file"));
359       } else {
360         crypt_write_signed(a, s, tmpfp);
361         rewind(tmpfp);
362         for (i = 0; i < sigcnt; i++) {
363           if (sigs[i]->type == TYPEAPPLICATION) {
364             int subtype;
365
366             switch ((subtype = mime_which_token(sigs[i]->subtype, -1))) {
367               case MIME_PGP_SIGNATURE:
368               case MIME_X_PKCS7_SIGNATURE:
369               case MIME_PKCS7_SIGNATURE:
370                 if (crypt_verify_one(sigs[i], s, tmpfp, subtype != MIME_PGP_SIGNATURE) != 0)
371                   goodsig = 0;
372
373                 m_fclose(&tmpfp);
374                 continue;
375
376               default:
377                 break;
378             }
379           }
380
381           state_printf(s, _("[-- Warning: "
382                             "We can't verify %s/%s signatures. --]\n\n"),
383                        TYPE (sigs[i]), sigs[i]->subtype);
384         }
385       }
386
387       b->goodsig = goodsig;
388       b->badsig  = !goodsig;
389
390       /* Now display the signed body */
391       state_attach_puts(_("[-- The following data is signed --]\n\n"), s);
392
393       p_delete(&sigs);
394     } else {
395       state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"),
396                         s);
397     }
398   }
399
400   rc = mutt_body_handler (a, s);
401
402   if (s->flags & M_DISPLAY && sigcnt)
403     state_attach_puts (_("\n[-- End of signed data --]\n"), s);
404
405   return (rc);
406 }