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