mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-1997 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 1999-2000,2002-2004,2006 Thomas Roessler <roessler@does-not-exist.org>
4 * Copyright (C) 2001 Thomas Roessler <roessler@does-not-exist.org>
5 * Oliver Ehli <elmy@acm.org>
6 * Copyright (C) 2003 Werner Koch <wk@gnupg.org>
7 * Copyright (C) 2004 g10code GmbH
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23
24#if HAVE_CONFIG_H
25# include "config.h"
26#endif
27
28#include "mutt.h"
29#include "mutt_curses.h"
30#include "mime.h"
31#include "copy.h"
32#include "mutt_crypt.h"
33
34#ifdef USE_AUTOCRYPT
35#include "autocrypt.h"
36#endif
37
38#include <sys/wait.h>
39#include <string.h>
40#include <stdlib.h>
41#include <unistd.h>
42#include <sys/stat.h>
43#include <errno.h>
44#include <ctype.h>
45
46#ifdef HAVE_LOCALE_H
47#include <locale.h>
48#endif
49
50#ifdef HAVE_SYS_TIME_H
51# include <sys/time.h>
52#endif
53
54#ifdef HAVE_SYS_RESOURCE_H
55# include <sys/resource.h>
56#endif
57
58
59/* print the current time to avoid spoofing of the signature output */
60void crypt_current_time(STATE *s, char *app_name)
61{
62 time_t t;
63 char p[STRING], tmp[STRING];
64
65 if (!WithCrypto)
66 return;
67
68 if (option (OPTCRYPTTIMESTAMP))
69 {
70 t = time(NULL);
71 strftime (p, sizeof (p), _(" (current time: %c)"), localtime (&t));
72 }
73 else
74 *p = '\0';
75
76 snprintf (tmp, sizeof (tmp), _("[-- %s output follows%s --]\n"), NONULL(app_name), p);
77 state_attach_puts (tmp, s);
78}
79
80
81
82void crypt_forget_passphrase (void)
83{
84 if ((WithCrypto & APPLICATION_PGP))
85 crypt_pgp_void_passphrase ();
86
87 if ((WithCrypto & APPLICATION_SMIME))
88 crypt_smime_void_passphrase ();
89
90 if (WithCrypto)
91 mutt_message _("Passphrase(s) forgotten.");
92}
93
94
95#if defined(HAVE_SETRLIMIT) && (!defined(DEBUG))
96
97static void disable_coredumps (void)
98{
99 struct rlimit rl = {0, 0};
100 static short done = 0;
101
102 if (!done)
103 {
104 setrlimit (RLIMIT_CORE, &rl);
105 done = 1;
106 }
107}
108
109#endif /* HAVE_SETRLIMIT */
110
111
112int crypt_valid_passphrase(int flags)
113{
114 int ret = 0;
115
116# if defined(HAVE_SETRLIMIT) &&(!defined(DEBUG))
117 disable_coredumps ();
118# endif
119
120 if ((WithCrypto & APPLICATION_PGP) && (flags & APPLICATION_PGP))
121 ret = crypt_pgp_valid_passphrase ();
122
123 if ((WithCrypto & APPLICATION_SMIME) && (flags & APPLICATION_SMIME))
124 ret = crypt_smime_valid_passphrase ();
125
126 return ret;
127}
128
129
130/* In postpone mode, signing is automatically disabled. */
131int mutt_protect (HEADER *msg, char *keylist, int postpone)
132{
133 BODY *pbody = NULL, *tmp_pbody = NULL;
134 BODY *tmp_smime_pbody = NULL;
135 BODY *tmp_pgp_pbody = NULL;
136 ENVELOPE *protected_headers = NULL;
137 int security, sign, has_retainable_sig = 0;
138 int i;
139
140 if (!WithCrypto)
141 return -1;
142
143 security = msg->security;
144 sign = security & (AUTOCRYPT | SIGN);
145 if (postpone)
146 {
147 sign = 0;
148 security &= ~SIGN;
149 }
150
151 if (!(security & (ENCRYPT | AUTOCRYPT)) && !sign)
152 return 0;
153
154 if (sign &&
155 !(security & AUTOCRYPT) &&
156 !crypt_valid_passphrase (security))
157 return (-1);
158
159 if ((WithCrypto & APPLICATION_PGP) &&
160 !(security & AUTOCRYPT) &&
161 ((security & PGPINLINE) == PGPINLINE))
162 {
163 if ((msg->content->type != TYPETEXT) ||
164 ascii_strcasecmp (msg->content->subtype, "plain"))
165 {
166 if ((i = query_quadoption (OPT_PGPMIMEAUTO,
167 _("Inline PGP can't be used with attachments. Revert to PGP/MIME?"))) != MUTT_YES)
168 {
169 mutt_error _("Mail not sent: inline PGP can't be used with attachments.");
170 return -1;
171 }
172 }
173 else if (!mutt_strcasecmp ("flowed",
174 mutt_get_parameter ("format", msg->content->parameter)))
175 {
176 if ((i = query_quadoption (OPT_PGPMIMEAUTO,
177 _("Inline PGP can't be used with format=flowed. Revert to PGP/MIME?"))) != MUTT_YES)
178 {
179 mutt_error _("Mail not sent: inline PGP can't be used with format=flowed.");
180 return -1;
181 }
182 }
183 else
184 {
185 /* they really want to send it inline... go for it */
186 if (!isendwin ()) mutt_endwin _("Invoking PGP...");
187 pbody = crypt_pgp_traditional_encryptsign (msg->content, security, keylist);
188 if (pbody)
189 {
190 msg->content = pbody;
191 return 0;
192 }
193
194 /* otherwise inline won't work...ask for revert */
195 if ((i = query_quadoption (OPT_PGPMIMEAUTO, _("Message can't be sent inline. Revert to using PGP/MIME?"))) != MUTT_YES)
196 {
197 mutt_error _("Mail not sent.");
198 return -1;
199 }
200 }
201
202 /* go ahead with PGP/MIME */
203 }
204
205 if (!isendwin ()) mutt_endwin (NULL);
206
207 if ((WithCrypto & APPLICATION_SMIME))
208 tmp_smime_pbody = msg->content;
209 if ((WithCrypto & APPLICATION_PGP))
210 tmp_pgp_pbody = msg->content;
211
212 if (option (OPTCRYPTUSEPKA) && sign)
213 {
214 /* Set sender (necessary for e.g. PKA). */
215
216 if ((WithCrypto & APPLICATION_SMIME)
217 && (security & APPLICATION_SMIME))
218 crypt_smime_set_sender (msg->env->from->mailbox);
219 else if ((WithCrypto & APPLICATION_PGP)
220 && (security & APPLICATION_PGP))
221 crypt_pgp_set_sender (msg->env->from->mailbox);
222 }
223
224 if (option (OPTCRYPTPROTHDRSWRITE))
225 {
226 protected_headers = mutt_new_envelope ();
227 mutt_str_replace (&protected_headers->subject, msg->env->subject);
228 /* Note: if other headers get added, such as to, cc, then a call to
229 * mutt_env_to_intl() will need to be added here too. */
230 mutt_prepare_envelope (protected_headers, 0);
231
232 mutt_free_envelope (&msg->content->mime_headers);
233 msg->content->mime_headers = protected_headers;
234 }
235
236 /* A note about msg->content->mime_headers. If postpone or send
237 * fails, the mime_headers is cleared out before returning to the
238 * compose menu. So despite the "robustness" code above and in the
239 * gen_gossip_list function below, mime_headers will not be set when
240 * entering mutt_protect().
241 *
242 * This is important to note because the user could toggle
243 * $crypt_protected_headers_write or $autocrypt off back in the
244 * compose menu. We don't want mutt_write_rfc822_header() to write
245 * stale data from one option if the other is set.
246 */
247#ifdef USE_AUTOCRYPT
248 if (option (OPTAUTOCRYPT) &&
249 !postpone &&
250 (security & AUTOCRYPT))
251 {
252 mutt_autocrypt_generate_gossip_list (msg);
253 }
254#endif
255
256 if (sign)
257 {
258 if ((WithCrypto & APPLICATION_SMIME)
259 && (security & APPLICATION_SMIME))
260 {
261 if (!(tmp_pbody = crypt_smime_sign_message (msg->content)))
262 goto bail;
263 pbody = tmp_smime_pbody = tmp_pbody;
264 }
265
266 if ((WithCrypto & APPLICATION_PGP)
267 && (security & APPLICATION_PGP)
268 && (!(security & (ENCRYPT | AUTOCRYPT)) || option (OPTPGPRETAINABLESIG)))
269 {
270 if (!(tmp_pbody = crypt_pgp_sign_message (msg->content)))
271 goto bail;
272
273 has_retainable_sig = 1;
274 sign = 0;
275 pbody = tmp_pgp_pbody = tmp_pbody;
276 }
277
278 if (WithCrypto
279 && (security & APPLICATION_SMIME)
280 && (security & APPLICATION_PGP))
281 {
282 /* here comes the draft ;-) */
283 }
284 }
285
286
287 if (security & (ENCRYPT | AUTOCRYPT))
288 {
289 if ((WithCrypto & APPLICATION_SMIME)
290 && (security & APPLICATION_SMIME))
291 {
292 if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
293 keylist)))
294 {
295 /* signed ? free it! */
296 goto bail;
297 }
298 /* free tmp_body if messages was signed AND encrypted ... */
299 if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody)
300 {
301 /* detach and don't delete msg->content,
302 which tmp_smime_pbody->parts after signing. */
303 tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
304 msg->content->next = NULL;
305 mutt_free_body (&tmp_smime_pbody);
306 }
307 pbody = tmp_pbody;
308 }
309
310 if ((WithCrypto & APPLICATION_PGP)
311 && (security & APPLICATION_PGP))
312 {
313 if (!(pbody = crypt_pgp_encrypt_message (msg, tmp_pgp_pbody, keylist,
314 sign)))
315 {
316
317 /* did we perform a retainable signature? */
318 if (has_retainable_sig)
319 {
320 /* remove the outer multipart layer */
321 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
322 /* get rid of the signature */
323 mutt_free_body (&tmp_pgp_pbody->next);
324 }
325
326 goto bail;
327 }
328
329 /* destroy temporary signature envelope when doing retainable
330 * signatures.
331
332 */
333 if (has_retainable_sig)
334 {
335 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
336 mutt_free_body (&tmp_pgp_pbody->next);
337 }
338 }
339 }
340
341 if (pbody)
342 {
343 msg->content = pbody;
344 return 0;
345 }
346
347bail:
348 mutt_free_envelope (&msg->content->mime_headers);
349 return -1;
350}
351
352
353
354
355int mutt_is_multipart_signed (BODY *b)
356{
357 char *p;
358
359 if (!b || !(b->type == TYPEMULTIPART) ||
360 !b->subtype || ascii_strcasecmp(b->subtype, "signed"))
361 return 0;
362
363 if (!(p = mutt_get_parameter("protocol", b->parameter)))
364 return 0;
365
366 if (!(ascii_strcasecmp (p, "multipart/mixed")))
367 return SIGN;
368
369 if ((WithCrypto & APPLICATION_PGP)
370 && !(ascii_strcasecmp (p, "application/pgp-signature")))
371 return PGPSIGN;
372
373 if ((WithCrypto & APPLICATION_SMIME)
374 && !(ascii_strcasecmp (p, "application/x-pkcs7-signature")))
375 return SMIMESIGN;
376 if ((WithCrypto & APPLICATION_SMIME)
377 && !(ascii_strcasecmp (p, "application/pkcs7-signature")))
378 return SMIMESIGN;
379
380 return 0;
381}
382
383
384int mutt_is_multipart_encrypted (BODY *b)
385{
386 if ((WithCrypto & APPLICATION_PGP))
387 {
388 char *p;
389
390 if (!b || b->type != TYPEMULTIPART ||
391 !b->subtype || ascii_strcasecmp (b->subtype, "encrypted") ||
392 !(p = mutt_get_parameter ("protocol", b->parameter)) ||
393 ascii_strcasecmp (p, "application/pgp-encrypted"))
394 return 0;
395
396 return PGPENCRYPT;
397 }
398
399 return 0;
400}
401
402
403int mutt_is_valid_multipart_pgp_encrypted (BODY *b)
404{
405 if (! mutt_is_multipart_encrypted (b))
406 return 0;
407
408 b = b->parts;
409 if (!b || b->type != TYPEAPPLICATION ||
410 !b->subtype || ascii_strcasecmp (b->subtype, "pgp-encrypted"))
411 return 0;
412
413 b = b->next;
414 if (!b || b->type != TYPEAPPLICATION ||
415 !b->subtype || ascii_strcasecmp (b->subtype, "octet-stream"))
416 return 0;
417
418 return PGPENCRYPT;
419}
420
421
422/*
423 * This checks for the malformed layout caused by MS Exchange in
424 * some cases:
425 * <multipart/mixed>
426 * <text/plain>
427 * <application/pgp-encrypted> [BASE64-encoded]
428 * <application/octet-stream> [BASE64-encoded]
429 * See ticket #3742
430 */
431int mutt_is_malformed_multipart_pgp_encrypted (BODY *b)
432{
433 if (!(WithCrypto & APPLICATION_PGP))
434 return 0;
435
436 if (!b || b->type != TYPEMULTIPART ||
437 !b->subtype || ascii_strcasecmp (b->subtype, "mixed"))
438 return 0;
439
440 b = b->parts;
441 if (!b || b->type != TYPETEXT ||
442 !b->subtype || ascii_strcasecmp (b->subtype, "plain") ||
443 b->length != 0)
444 return 0;
445
446 b = b->next;
447 if (!b || b->type != TYPEAPPLICATION ||
448 !b->subtype || ascii_strcasecmp (b->subtype, "pgp-encrypted"))
449 return 0;
450
451 b = b->next;
452 if (!b || b->type != TYPEAPPLICATION ||
453 !b->subtype || ascii_strcasecmp (b->subtype, "octet-stream"))
454 return 0;
455
456 b = b->next;
457 if (b)
458 return 0;
459
460 return PGPENCRYPT;
461}
462
463
464int mutt_is_application_pgp (BODY *m)
465{
466 int t = 0;
467 char *p;
468
469 if (m->type == TYPEAPPLICATION)
470 {
471 if (!ascii_strcasecmp (m->subtype, "pgp") || !ascii_strcasecmp (m->subtype, "x-pgp-message"))
472 {
473 if ((p = mutt_get_parameter ("x-action", m->parameter))
474 && (!ascii_strcasecmp (p, "sign") || !ascii_strcasecmp (p, "signclear")))
475 t |= PGPSIGN;
476
477 if ((p = mutt_get_parameter ("format", m->parameter)) &&
478 !ascii_strcasecmp (p, "keys-only"))
479 t |= PGPKEY;
480
481 if (!t) t |= PGPENCRYPT; /* not necessarily correct, but... */
482 }
483
484 if (!ascii_strcasecmp (m->subtype, "pgp-signed"))
485 t |= PGPSIGN;
486
487 if (!ascii_strcasecmp (m->subtype, "pgp-keys"))
488 t |= PGPKEY;
489 }
490 else if (m->type == TYPETEXT && ascii_strcasecmp ("plain", m->subtype) == 0)
491 {
492 if (((p = mutt_get_parameter ("x-mutt-action", m->parameter))
493 || (p = mutt_get_parameter ("x-action", m->parameter))
494 || (p = mutt_get_parameter ("action", m->parameter)))
495 && !ascii_strncasecmp ("pgp-sign", p, 8))
496 t |= PGPSIGN;
497 else if (p && !ascii_strncasecmp ("pgp-encrypt", p, 11))
498 t |= PGPENCRYPT;
499 else if (p && !ascii_strncasecmp ("pgp-keys", p, 7))
500 t |= PGPKEY;
501 }
502 if (t)
503 t |= PGPINLINE;
504
505 return t;
506}
507
508int mutt_is_application_smime (BODY *m)
509{
510 char *t=NULL;
511 int len, complain=0;
512
513 if (!m)
514 return 0;
515
516 if ((m->type & TYPEAPPLICATION) && m->subtype)
517 {
518 /* S/MIME MIME types don't need x- anymore, see RFC2311 */
519 if (!ascii_strcasecmp (m->subtype, "x-pkcs7-mime") ||
520 !ascii_strcasecmp (m->subtype, "pkcs7-mime"))
521 {
522 if ((t = mutt_get_parameter ("smime-type", m->parameter)))
523 {
524 if (!ascii_strcasecmp (t, "enveloped-data"))
525 return SMIMEENCRYPT;
526 else if (!ascii_strcasecmp (t, "signed-data"))
527 return (SMIMESIGN|SMIMEOPAQUE);
528 else return 0;
529 }
530 /* Netscape 4.7 uses
531 * Content-Description: S/MIME Encrypted Message
532 * instead of Content-Type parameter
533 */
534 if (!ascii_strcasecmp (m->description, "S/MIME Encrypted Message"))
535 return SMIMEENCRYPT;
536 complain = 1;
537 }
538 else if (ascii_strcasecmp (m->subtype, "octet-stream"))
539 return 0;
540
541 t = mutt_get_parameter ("name", m->parameter);
542
543 if (!t) t = m->d_filename;
544 if (!t) t = m->filename;
545 if (!t)
546 {
547 if (complain)
548 mutt_message (_("S/MIME messages with no hints on content are unsupported."));
549 return 0;
550 }
551
552 /* no .p7c, .p10 support yet. */
553
554 len = mutt_strlen (t) - 4;
555 if (len > 0 && *(t+len) == '.')
556 {
557 len++;
558 if (!ascii_strcasecmp ((t+len), "p7m"))
559 /* Not sure if this is the correct thing to do, but
560 it's required for compatibility with Outlook */
561 return (SMIMESIGN|SMIMEOPAQUE);
562 else if (!ascii_strcasecmp ((t+len), "p7s"))
563 return (SMIMESIGN|SMIMEOPAQUE);
564 }
565 }
566
567 return 0;
568}
569
570
571
572
573
574
575int crypt_query (BODY *m)
576{
577 int t = 0;
578
579 if (!WithCrypto)
580 return 0;
581
582 if (!m)
583 return 0;
584
585 if (m->type == TYPEAPPLICATION)
586 {
587 if ((WithCrypto & APPLICATION_PGP))
588 t |= mutt_is_application_pgp(m);
589
590 if ((WithCrypto & APPLICATION_SMIME))
591 {
592 t |= mutt_is_application_smime(m);
593 if (t && m->goodsig) t |= GOODSIGN;
594 if (t && m->badsig) t |= BADSIGN;
595 }
596 }
597 else if ((WithCrypto & APPLICATION_PGP) && m->type == TYPETEXT)
598 {
599 t |= mutt_is_application_pgp (m);
600 if (t && m->goodsig)
601 t |= GOODSIGN;
602 }
603
604 if (m->type == TYPEMULTIPART)
605 {
606 t |= mutt_is_multipart_encrypted(m);
607 t |= mutt_is_multipart_signed (m);
608 t |= mutt_is_malformed_multipart_pgp_encrypted (m);
609
610 if (t && m->goodsig)
611 t |= GOODSIGN;
612#ifdef USE_AUTOCRYPT
613 if (t && m->is_autocrypt)
614 t |= AUTOCRYPT;
615#endif
616 }
617
618 if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE)
619 {
620 BODY *p;
621 int u, v, w;
622
623 u = m->parts ? 0xffffffff : 0; /* Bits set in all parts */
624 w = 0; /* Bits set in any part */
625
626 for (p = m->parts; p; p = p->next)
627 {
628 v = crypt_query (p);
629 u &= v; w |= v;
630 }
631 t |= u | (w & ~GOODSIGN);
632
633 if ((w & GOODSIGN) && !(u & GOODSIGN))
634 t |= PARTSIGN;
635 }
636
637 return t;
638}
639
640
641
642
643int crypt_write_signed(BODY *a, STATE *s, const char *tempfile)
644{
645 FILE *fp;
646 int c;
647 short hadcr;
648 size_t bytes;
649
650 if (!WithCrypto)
651 return -1;
652
653 if (!(fp = safe_fopen (tempfile, "w")))
654 {
655 mutt_perror (tempfile);
656 return -1;
657 }
658
659 fseeko (s->fpin, a->hdr_offset, 0);
660 bytes = a->length + a->offset - a->hdr_offset;
661 hadcr = 0;
662 while (bytes > 0)
663 {
664 if ((c = fgetc (s->fpin)) == EOF)
665 break;
666
667 bytes--;
668
669 if (c == '\r')
670 hadcr = 1;
671 else
672 {
673 if (c == '\n' && !hadcr)
674 fputc ('\r', fp);
675
676 hadcr = 0;
677 }
678
679 fputc (c, fp);
680
681 }
682 safe_fclose (&fp);
683
684 return 0;
685}
686
687
688
689void convert_to_7bit (BODY *a)
690{
691 if (!WithCrypto)
692 return;
693
694 while (a)
695 {
696 if (a->type == TYPEMULTIPART)
697 {
698 if (a->encoding != ENC7BIT)
699 {
700 a->encoding = ENC7BIT;
701 convert_to_7bit(a->parts);
702 }
703 else if ((WithCrypto & APPLICATION_PGP) && option (OPTPGPSTRICTENC))
704 convert_to_7bit (a->parts);
705 }
706 else if (a->type == TYPEMESSAGE &&
707 ascii_strcasecmp(a->subtype, "delivery-status"))
708 {
709 if (a->encoding != ENC7BIT)
710 mutt_message_to_7bit (a, NULL);
711 }
712 else if (a->encoding == ENC8BIT)
713 a->encoding = ENCQUOTEDPRINTABLE;
714 else if (a->encoding == ENCBINARY)
715 a->encoding = ENCBASE64;
716 else if (a->content && a->encoding != ENCBASE64 &&
717 (a->content->from || (a->content->space &&
718 option (OPTPGPSTRICTENC))))
719 a->encoding = ENCQUOTEDPRINTABLE;
720 a = a->next;
721 }
722}
723
724
725
726
727void crypt_extract_keys_from_messages (HEADER * h)
728{
729 int i;
730 BUFFER *tempfname = NULL;
731 char *mbox;
732 ADDRESS *tmp = NULL;
733 FILE *fpout;
734
735 if (!WithCrypto)
736 return;
737
738 tempfname = mutt_buffer_pool_get ();
739 mutt_buffer_mktemp (tempfname);
740 if (!(fpout = safe_fopen (mutt_b2s (tempfname), "w")))
741 {
742 mutt_perror (mutt_b2s (tempfname));
743 goto cleanup;
744 }
745
746 if ((WithCrypto & APPLICATION_PGP))
747 set_option (OPTDONTHANDLEPGPKEYS);
748
749 if (!h)
750 {
751 for (i = 0; i < Context->vcount; i++)
752 {
753 if (Context->hdrs[Context->v2r[i]]->tagged)
754 {
755 mutt_parse_mime_message (Context, Context->hdrs[Context->v2r[i]]);
756 if (Context->hdrs[Context->v2r[i]]->security & ENCRYPT &&
757 !crypt_valid_passphrase (Context->hdrs[Context->v2r[i]]->security))
758 {
759 safe_fclose (&fpout);
760 break;
761 }
762
763 if ((WithCrypto & APPLICATION_PGP)
764 && (Context->hdrs[Context->v2r[i]]->security & APPLICATION_PGP))
765 {
766 mutt_copy_message (fpout, Context, Context->hdrs[Context->v2r[i]],
767 MUTT_CM_DECODE|MUTT_CM_CHARCONV, 0);
768 fflush(fpout);
769
770 mutt_endwin (_("Trying to extract PGP keys...\n"));
771 crypt_pgp_invoke_import (mutt_b2s (tempfname));
772 }
773
774 if ((WithCrypto & APPLICATION_SMIME)
775 && (Context->hdrs[Context->v2r[i]]->security & APPLICATION_SMIME))
776 {
777 if (Context->hdrs[Context->v2r[i]]->security & ENCRYPT)
778 mutt_copy_message (fpout, Context, Context->hdrs[Context->v2r[i]],
779 MUTT_CM_NOHEADER|MUTT_CM_DECODE_CRYPT
780 |MUTT_CM_DECODE_SMIME, 0);
781 else
782 mutt_copy_message (fpout, Context,
783 Context->hdrs[Context->v2r[i]], 0, 0);
784 fflush(fpout);
785
786 if (Context->hdrs[Context->v2r[i]]->env->from)
787 tmp = mutt_expand_aliases (Context->hdrs[Context->v2r[i]]->env->from);
788 else if (Context->hdrs[Context->v2r[i]]->env->sender)
789 tmp = mutt_expand_aliases (Context->hdrs[Context->v2r[i]]->env->sender);
790 mbox = tmp ? tmp->mailbox : NULL;
791 if (mbox)
792 {
793 mutt_endwin (_("Trying to extract S/MIME certificates...\n"));
794 crypt_smime_invoke_import (mutt_b2s (tempfname), mbox);
795 tmp = NULL;
796 }
797 }
798
799 rewind (fpout);
800 }
801 }
802 }
803 else
804 {
805 mutt_parse_mime_message (Context, h);
806 if (!(h->security & ENCRYPT && !crypt_valid_passphrase (h->security)))
807 {
808 if ((WithCrypto & APPLICATION_PGP)
809 && (h->security & APPLICATION_PGP))
810 {
811 mutt_copy_message (fpout, Context, h, MUTT_CM_DECODE|MUTT_CM_CHARCONV, 0);
812 fflush(fpout);
813 mutt_endwin (_("Trying to extract PGP keys...\n"));
814 crypt_pgp_invoke_import (mutt_b2s (tempfname));
815 }
816
817 if ((WithCrypto & APPLICATION_SMIME)
818 && (h->security & APPLICATION_SMIME))
819 {
820 if (h->security & ENCRYPT)
821 mutt_copy_message (fpout, Context, h,
822 MUTT_CM_NOHEADER | MUTT_CM_DECODE_CRYPT | MUTT_CM_DECODE_SMIME,
823 0);
824 else
825 mutt_copy_message (fpout, Context, h, 0, 0);
826
827 fflush(fpout);
828 if (h->env->from) tmp = mutt_expand_aliases (h->env->from);
829 else if (h->env->sender) tmp = mutt_expand_aliases (h->env->sender);
830 mbox = tmp ? tmp->mailbox : NULL;
831 if (mbox) /* else ? */
832 {
833 mutt_message (_("Trying to extract S/MIME certificates...\n"));
834 crypt_smime_invoke_import (mutt_b2s (tempfname), mbox);
835 }
836 }
837 }
838 }
839
840 safe_fclose (&fpout);
841 if (isendwin())
842 mutt_any_key_to_continue (NULL);
843
844 mutt_unlink (mutt_b2s (tempfname));
845
846 if ((WithCrypto & APPLICATION_PGP))
847 unset_option (OPTDONTHANDLEPGPKEYS);
848
849cleanup:
850 mutt_buffer_pool_release (&tempfname);
851}
852
853
854
855int crypt_get_keys (HEADER *msg, char **keylist, int oppenc_mode)
856{
857 ADDRESS *adrlist = NULL, *last = NULL;
858 const char *fqdn = mutt_fqdn (1);
859 char *self_encrypt = NULL;
860 size_t keylist_size;
861
862 /* Do a quick check to make sure that we can find all of the encryption
863 * keys if the user has requested this service.
864 */
865
866 if (!WithCrypto)
867 return 0;
868
869 *keylist = NULL;
870
871#ifdef USE_AUTOCRYPT
872 if (!oppenc_mode && (msg->security & AUTOCRYPT))
873 {
874 if (mutt_autocrypt_ui_recommendation (msg, keylist) <= AUTOCRYPT_REC_NO)
875 return (-1);
876 return (0);
877 }
878#endif
879
880 if ((WithCrypto & APPLICATION_PGP))
881 set_option (OPTPGPCHECKTRUST);
882
883 last = rfc822_append (&adrlist, msg->env->to, 0);
884 last = rfc822_append (last ? &last : &adrlist, msg->env->cc, 0);
885 rfc822_append (last ? &last : &adrlist, msg->env->bcc, 0);
886
887 if (fqdn)
888 rfc822_qualify (adrlist, fqdn);
889 adrlist = mutt_remove_duplicates (adrlist);
890
891 if (oppenc_mode || (msg->security & ENCRYPT))
892 {
893 if ((WithCrypto & APPLICATION_PGP)
894 && (msg->security & APPLICATION_PGP))
895 {
896 if ((*keylist = crypt_pgp_findkeys (adrlist, oppenc_mode)) == NULL)
897 {
898 rfc822_free_address (&adrlist);
899 return (-1);
900 }
901 unset_option (OPTPGPCHECKTRUST);
902 if (option (OPTPGPSELFENCRYPT))
903 self_encrypt = PgpDefaultKey;
904 }
905 if ((WithCrypto & APPLICATION_SMIME)
906 && (msg->security & APPLICATION_SMIME))
907 {
908 if ((*keylist = crypt_smime_findkeys (adrlist, oppenc_mode)) == NULL)
909 {
910 rfc822_free_address (&adrlist);
911 return (-1);
912 }
913 if (option (OPTSMIMESELFENCRYPT))
914 self_encrypt = SmimeDefaultKey;
915 }
916 }
917
918 if (!oppenc_mode && self_encrypt)
919 {
920 keylist_size = mutt_strlen (*keylist);
921 safe_realloc (keylist, keylist_size + mutt_strlen (self_encrypt) + 2);
922 sprintf (*keylist + keylist_size, " %s", self_encrypt); /* __SPRINTF_CHECKED__ */
923 }
924
925 rfc822_free_address (&adrlist);
926
927 return (0);
928}
929
930
931/*
932 * Check if all recipients keys can be automatically determined.
933 * Enable encryption if they can, otherwise disable encryption.
934 */
935
936void crypt_opportunistic_encrypt(HEADER *msg)
937{
938 char *pgpkeylist = NULL;
939
940 if (!WithCrypto)
941 return;
942
943 if (! (option (OPTCRYPTOPPORTUNISTICENCRYPT) && (msg->security & OPPENCRYPT)) )
944 return;
945
946 crypt_get_keys (msg, &pgpkeylist, 1);
947 if (pgpkeylist != NULL )
948 {
949 msg->security |= ENCRYPT;
950 FREE (&pgpkeylist);
951 }
952 else
953 {
954 msg->security &= ~ENCRYPT;
955 }
956}
957
958
959
960static void crypt_fetch_signatures (BODY ***signatures, BODY *a, int *n)
961{
962 if (!WithCrypto)
963 return;
964
965 for (; a; a = a->next)
966 {
967 if (a->type == TYPEMULTIPART)
968 crypt_fetch_signatures (signatures, a->parts, n);
969 else
970 {
971 if ((*n % 5) == 0)
972 safe_realloc (signatures, (*n + 6) * sizeof (BODY **));
973
974 (*signatures)[(*n)++] = a;
975 }
976 }
977}
978
979int mutt_should_hide_protected_subject (HEADER *h)
980{
981 if (option (OPTCRYPTPROTHDRSWRITE) &&
982 (h->security & (ENCRYPT | AUTOCRYPT)) &&
983 !(h->security & INLINE) &&
984 ProtHdrSubject)
985 return 1;
986
987 return 0;
988}
989
990int mutt_protected_headers_handler (BODY *a, STATE *s)
991{
992 if (option (OPTCRYPTPROTHDRSREAD) && a->mime_headers)
993 {
994 if (a->mime_headers->subject)
995 {
996 if ((s->flags & MUTT_DISPLAY) && option (OPTWEED) &&
997 mutt_matches_ignore ("subject", Ignore) &&
998 !mutt_matches_ignore ("subject", UnIgnore))
999 return 0;
1000
1001 state_mark_protected_header (s);
1002 mutt_write_one_header (s->fpout, "Subject", a->mime_headers->subject,
1003 s->prefix,
1004 mutt_window_wrap_cols (MuttIndexWindow, Wrap),
1005 (s->flags & MUTT_DISPLAY) ? CH_DISPLAY : 0);
1006 state_puts ("\n", s);
1007 }
1008 }
1009
1010 return 0;
1011}
1012
1013/*
1014 * This routine verifies a "multipart/signed" body.
1015 */
1016
1017int mutt_signed_handler (BODY *a, STATE *s)
1018{
1019 BUFFER *tempfile = NULL;
1020 int signed_type;
1021 int inconsistent = 0;
1022
1023 BODY *b = a;
1024 BODY **signatures = NULL;
1025 int sigcnt = 0;
1026 int i;
1027 short goodsig = 1;
1028 int rc = 0;
1029
1030 if (!WithCrypto)
1031 return -1;
1032
1033 a = a->parts;
1034 signed_type = mutt_is_multipart_signed (b);
1035 if (!signed_type)
1036 {
1037 /* A null protocol value is already checked for in mutt_body_handler() */
1038 state_printf (s, _("[-- Error: "
1039 "Unknown multipart/signed protocol %s! --]\n\n"),
1040 mutt_get_parameter ("protocol", b->parameter));
1041 return mutt_body_handler (a, s);
1042 }
1043
1044 if (!(a && a->next))
1045 inconsistent = 1;
1046 else
1047 {
1048 switch (signed_type)
1049 {
1050 case SIGN:
1051 if (a->next->type != TYPEMULTIPART ||
1052 ascii_strcasecmp (a->next->subtype, "mixed"))
1053 inconsistent = 1;
1054 break;
1055 case PGPSIGN:
1056 if (a->next->type != TYPEAPPLICATION ||
1057 ascii_strcasecmp (a->next->subtype, "pgp-signature"))
1058 inconsistent = 1;
1059 break;
1060 case SMIMESIGN:
1061 if (a->next->type != TYPEAPPLICATION ||
1062 (ascii_strcasecmp (a->next->subtype, "x-pkcs7-signature") &&
1063 ascii_strcasecmp (a->next->subtype, "pkcs7-signature")))
1064 inconsistent = 1;
1065 break;
1066 default:
1067 inconsistent = 1;
1068 }
1069 }
1070 if (inconsistent)
1071 {
1072 state_attach_puts (_("[-- Error: "
1073 "Missing or bad-format multipart/signed signature!"
1074 " --]\n\n"),
1075 s);
1076 return mutt_body_handler (a, s);
1077 }
1078
1079 if (s->flags & MUTT_DISPLAY)
1080 {
1081
1082 crypt_fetch_signatures (&signatures, a->next, &sigcnt);
1083
1084 if (sigcnt)
1085 {
1086 tempfile = mutt_buffer_pool_get ();
1087 mutt_buffer_mktemp (tempfile);
1088 if (crypt_write_signed (a, s, mutt_b2s (tempfile)) == 0)
1089 {
1090 for (i = 0; i < sigcnt; i++)
1091 {
1092 if ((WithCrypto & APPLICATION_PGP)
1093 && signatures[i]->type == TYPEAPPLICATION
1094 && !ascii_strcasecmp (signatures[i]->subtype, "pgp-signature"))
1095 {
1096 if (crypt_pgp_verify_one (signatures[i], s, mutt_b2s (tempfile)) != 0)
1097 goodsig = 0;
1098
1099 continue;
1100 }
1101
1102 if ((WithCrypto & APPLICATION_SMIME)
1103 && signatures[i]->type == TYPEAPPLICATION
1104 && (!ascii_strcasecmp(signatures[i]->subtype, "x-pkcs7-signature")
1105 || !ascii_strcasecmp(signatures[i]->subtype, "pkcs7-signature")))
1106 {
1107 if (crypt_smime_verify_one (signatures[i], s, mutt_b2s (tempfile)) != 0)
1108 goodsig = 0;
1109
1110 continue;
1111 }
1112
1113 state_printf (s, _("[-- Warning: "
1114 "We can't verify %s/%s signatures. --]\n\n"),
1115 TYPE(signatures[i]), signatures[i]->subtype);
1116 }
1117 }
1118
1119 mutt_unlink (mutt_b2s (tempfile));
1120 mutt_buffer_pool_release (&tempfile);
1121
1122 b->goodsig = goodsig;
1123 b->badsig = !goodsig;
1124
1125 /* Now display the signed body */
1126 state_attach_puts (_("[-- The following data is signed --]\n\n"), s);
1127
1128 mutt_protected_headers_handler (a, s);
1129
1130 FREE (&signatures);
1131 }
1132 else
1133 state_attach_puts (_("[-- Warning: Can't find any signatures. --]\n\n"), s);
1134 }
1135
1136 rc = mutt_body_handler (a, s);
1137
1138 if (s->flags & MUTT_DISPLAY && sigcnt)
1139 state_attach_puts (_("\n[-- End of signed data --]\n"), s);
1140
1141 return rc;
1142}
1143
1144
1145/* Obtain pointers to fingerprint or short or long key ID, if any.
1146 * See mutt_crypt.h for details.
1147 */
1148const char* crypt_get_fingerprint_or_id (char *p, const char **pphint,
1149 const char **ppl, const char **pps)
1150{
1151 const char *ps, *pl, *phint;
1152 char *pfcopy, *pf, *s1, *s2;
1153 char c;
1154 int isid;
1155 size_t hexdigits;
1156
1157 /* User input may be partial name, fingerprint or short or long key ID,
1158 * independent of OPTPGPLONGIDS.
1159 * Fingerprint without spaces is 40 hex digits (SHA-1) or 32 hex digits (MD5).
1160 * Strip leading "0x" for key ID detection and prepare pl and ps to indicate
1161 * if an ID was found and to simplify logic in the key loop's inner
1162 * condition of the caller. */
1163
1164 pf = mutt_skip_whitespace (p);
1165 if (!mutt_strncasecmp (pf, "0x", 2))
1166 pf += 2;
1167
1168 /* Check if a fingerprint is given, must be hex digits only, blanks
1169 * separating groups of 4 hex digits are allowed. Also pre-check for ID. */
1170 isid = 2; /* unknown */
1171 hexdigits = 0;
1172 s1 = pf;
1173 do
1174 {
1175 c = *(s1++);
1176 if (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'))
1177 {
1178 ++hexdigits;
1179 if (isid == 2)
1180 isid = 1; /* it is an ID so far */
1181 }
1182 else if (c)
1183 {
1184 isid = 0; /* not an ID */
1185 if (c == ' ' && ((hexdigits % 4) == 0))
1186 ; /* skip blank before or after 4 hex digits */
1187 else
1188 break; /* any other character or position */
1189 }
1190 } while (c);
1191
1192 /* If at end of input, check for correct fingerprint length and copy if. */
1193 pfcopy = (!c && ((hexdigits == 40) || (hexdigits == 32)) ? safe_strdup (pf) : NULL);
1194
1195 if (pfcopy)
1196 {
1197 /* Use pfcopy to strip all spaces from fingerprint and as hint. */
1198 s1 = s2 = pfcopy;
1199 do
1200 {
1201 *(s1++) = *(s2 = mutt_skip_whitespace (s2));
1202 } while (*(s2++));
1203
1204 phint = pfcopy;
1205 ps = pl = NULL;
1206 }
1207 else
1208 {
1209 phint = p;
1210 ps = pl = NULL;
1211 if (isid == 1)
1212 {
1213 if (mutt_strlen (pf) == 16)
1214 pl = pf; /* long key ID */
1215 else if (mutt_strlen (pf) == 8)
1216 ps = pf; /* short key ID */
1217 }
1218 }
1219
1220 *pphint = phint;
1221 *ppl = pl;
1222 *pps = ps;
1223 return pfcopy;
1224}
1225
1226
1227/*
1228 * Used by pgp_findKeys and find_keys to check if a crypt-hook
1229 * value is a key id.
1230 */
1231
1232short crypt_is_numerical_keyid (const char *s)
1233{
1234 /* or should we require the "0x"? */
1235 if (strncmp (s, "0x", 2) == 0)
1236 s += 2;
1237 if (strlen (s) % 8)
1238 return 0;
1239 while (*s)
1240 if (strchr ("0123456789ABCDEFabcdef", *s++) == NULL)
1241 return 0;
1242
1243 return 1;
1244}