2 * Copyright notice from original mutt:
3 * crypt-gpgme.c - GPGME based crypto operations
4 * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
5 * Copyright (C) 1998,1999,2000 Thomas Roessler <roessler@guug.de>
6 * Copyright (C) 2001 Thomas Roessler <roessler@guug.de>
7 * Oliver Ehli <elmy@acm.org>
8 * Copyright (C) 2002, 2003, 2004 g10 Code GmbH
11 * Copyright © 2006 Pierre Habouzit
14 #include <lib-lib/lib-lib.h>
18 #include <lib-mime/mime.h>
19 #include <lib-ui/curses.h>
20 #include <lib-ui/enter.h>
21 #include <lib-ui/menu.h>
22 #include <lib-mx/mx.h>
30 #include "recvattach.h"
33 /* Values used for comparing addresses. */
34 #define CRYPT_KV_VALID 1
35 #define CRYPT_KV_ADDR 2
36 #define CRYPT_KV_STRING 4
37 #define CRYPT_KV_STRONGID 8
38 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
45 /* We work based on user IDs, getting from a user ID to the key is
46 check and does not need any memory (gpgme uses reference counting). */
47 typedef struct cryptkey_t {
48 struct cryptkey_t *next;
49 int idx; /* and the user ID at this index */
50 int flags; /* global and per uid flags (for convenience) */
52 const char *uid; /* and for convenience point to this user ID */
55 DO_INIT(cryptkey_t, cryptkey);
56 static void cryptkey_wipe(cryptkey_t *key) {
57 gpgme_key_release(key->kobj);
59 DO_NEW(cryptkey_t, cryptkey);
60 DO_DELETE(cryptkey_t, cryptkey);
61 DO_SLIST(cryptkey_t, key, cryptkey_delete);
63 static cryptkey_t *cryptkey_dup(const cryptkey_t *k)
65 cryptkey_t *res = cryptkey_new();
68 gpgme_key_ref(k->kobj);
72 typedef struct crypt_entry {
77 static gpgme_key_t signature_key = NULL;
79 static void convert_to_7bit (BODY * a)
81 for (; a; a = a->next) {
82 int tok = mime_which_token(a->subtype, -1);
84 if (a->type == TYPEMULTIPART) {
85 a->encoding = ENC7BIT;
86 convert_to_7bit(a->parts);
87 } else if (a->type == TYPEMESSAGE && tok == MIME_DELIVERY_STATUS) {
88 if (a->encoding != ENC7BIT)
89 mutt_message_to_7bit(a, NULL);
90 } else if (a->encoding == ENC8BIT) {
91 a->encoding = ENCQUOTEDPRINTABLE;
92 } else if (a->encoding == ENCBINARY) {
93 a->encoding = ENCBASE64;
94 } else if (a->content && a->encoding != ENCBASE64
95 && (a->content->from || a->content->space))
97 a->encoding = ENCQUOTEDPRINTABLE;
102 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
103 FP. Convert the character set. */
104 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
108 tstr = p_dupstr(buf, len);
109 mutt_convert_string(&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
114 /* Return the keyID for the key K. Note that this string is valid as
115 long as K is valid */
116 static const char *crypt_keyid (cryptkey_t * k)
118 if (k->kobj && k->kobj->subkeys) {
119 const char *s = k->kobj->subkeys->keyid;
120 return m_strlen(s) == 16 ? s + 8 : s;
126 /* Return the hexstring fingerprint from the key K. */
127 static const char *crypt_fpr (cryptkey_t * k)
129 return k->kobj && k->kobj->subkeys ? k->kobj->subkeys->fpr : "";
132 /* Parse FLAGS and return a statically allocated(!) string with them. */
133 static char *crypt_key_abilities (int flags)
137 if (!(flags & KEYFLAG_CANENCRYPT))
139 else if (flags & KEYFLAG_PREFER_SIGNING)
144 if (!(flags & KEYFLAG_CANSIGN))
146 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
156 /* Parse FLAGS and return a character describing the most important flag. */
157 static char crypt_flags (int flags)
159 if (flags & KEYFLAG_REVOKED)
161 else if (flags & KEYFLAG_EXPIRED)
163 else if (flags & KEYFLAG_DISABLED)
165 else if (flags & KEYFLAG_CRITICAL)
171 /* Return true whe validity of KEY is sufficient. */
172 static int crypt_id_is_strong (cryptkey_t * key)
177 if (key->flags & KEYFLAG_ISX509)
180 for (i = 0, uid = key->kobj->uids; uid; i++, uid = uid->next) {
182 return uid->validity == GPGME_VALIDITY_FULL
183 || uid->validity == GPGME_VALIDITY_ULTIMATE;
190 /* Return a bit vector describing how well the addresses ADDR and
191 U_ADDR match and whether KEY is valid. */
193 crypt_id_matches_addr(address_t *addr, address_t *u_addr, cryptkey_t *key)
197 if (!(key->flags & KEYFLAG_CANTUSE))
198 rv |= CRYPT_KV_VALID;
200 if (crypt_id_is_strong(key))
201 rv |= CRYPT_KV_STRONGID;
203 if (addr->mailbox && !m_strcasecmp(addr->mailbox, u_addr->mailbox))
206 if (addr->personal && m_strcasecmp(addr->personal, u_addr->personal))
207 rv |= CRYPT_KV_STRING;
213 /* Create a new gpgme context and return it. With FOR_SMIME set to
214 true, the protocol of the context is set to CMS. */
215 static gpgme_ctx_t create_gpgme_context(int for_smime)
220 err = gpgme_new (&ctx);
222 mutt_error(_("error creating gpgme context: %s\n"),
223 gpgme_strerror(err));
230 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
232 mutt_error(_("error enabling CMS protocol: %s\n"), gpgme_strerror(err));
239 /* Create a new gpgme data object. This is a wrapper to die on
241 static gpgme_data_t create_gpgme_data(void)
246 err = gpgme_data_new(&data);
248 mutt_error(_("error creating gpgme data object: %s\n"),
249 gpgme_strerror(err));
256 /* Create a new GPGME Data object from the mail body A. With CONVERT
257 passed as true, the lines are converted to CR,LF if required.
258 Return NULL on error or the gpgme_data_t object on success. */
259 static gpgme_data_t body_to_data_object(BODY *a, int convert)
265 if (!(fptmp = tmpfile())) {
266 mutt_perror (_("Can't create temporary file"));
270 mutt_write_mime_header(a, fptmp);
272 mutt_write_mime_body(a, fptmp);
279 data = create_gpgme_data();
281 while (fgets(buf + spare, sizeof(buf) - 1, fptmp)) {
282 int l = m_strlen(buf);
284 spare = buf[l - 1] != '\n';
285 if (!spare && (l <= 1 || buf[l - 2] != '\r')) {
289 gpgme_data_write(data, buf, l - spare);
294 gpgme_data_write(data, buf, 1);
295 gpgme_data_seek(data, 0, SEEK_SET);
300 err = gpgme_data_new_from_stream(&data, fptmp);
303 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
309 /* Create a GPGME data object from the stream FP but limit the object
310 to LENGTH bytes starting at OFFSET bytes from the beginning of the
312 static gpgme_data_t file_to_data_object(FILE *fp, long offset, long length)
317 err = gpgme_data_new_from_filepart(&data, NULL, fp, offset, length);
319 mutt_error(_("error allocating data object: %s\n"),
320 gpgme_strerror(err));
327 /* Write a GPGME data object to the stream FP. */
328 static int data_object_to_stream(gpgme_data_t data, FILE * fp)
334 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
335 ? gpgme_error_from_errno (errno) : 0);
337 mutt_error (_("error rewinding data object: %s\n"), gpgme_strerror (err));
341 while ((nread = gpgme_data_read(data, buf, sizeof (buf)))) {
342 /* fixme: we are not really converting CRLF to LF but just
343 skipping CR. Doing it correctly needs a more complex logic */
344 for (p = buf; nread; p++, nread--) {
350 mutt_perror ("[tempfile]");
355 mutt_error (_("error reading data object: %s\n"), strerror (errno));
361 /* Copy a data object to a newly created temporay file and return that
362 filename. Caller must free. With RET_FP not NULL, don't close the
363 stream but return it there. */
364 static char *data_object_to_tempfile(gpgme_data_t data, FILE ** ret_fp)
367 char tempfile[_POSIX_PATH_MAX];
371 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
373 mutt_perror (_("Can't create temporary file"));
377 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
378 ? gpgme_error_from_errno (errno) : 0);
382 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
383 if (fwrite (buf, nread, 1, fp) != 1) {
384 mutt_perror (_("Can't create temporary file"));
393 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
404 return m_strdup(tempfile);
408 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
410 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
416 if (!ctx || !r_key || !fpr)
417 return gpg_error (GPG_ERR_INV_VALUE);
419 if (strlen (fpr) < 8) /* We have at least a key ID. */
420 return gpg_error (GPG_ERR_INV_VALUE);
422 /* FIXME: We use our own context because we have to avoid the user's
423 I/O callback handlers. */
424 err = gpgme_new (&listctx);
427 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
428 err = gpgme_op_keylist_start (listctx, fpr, secret);
430 err = gpgme_op_keylist_next (listctx, r_key);
431 gpgme_release (listctx);
435 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
436 The keys must be space delimited. */
438 create_recipient_set(const char *keylist, gpgme_protocol_t protocol)
444 gpgme_key_t *rset = NULL;
445 unsigned int rset_n = 0;
446 gpgme_key_t key = NULL;
447 gpgme_ctx_t context = NULL;
449 err = gpgme_new (&context);
451 err = gpgme_set_protocol (context, protocol);
458 for (i = 0; *s && *s != ' ' && i < ssizeof(buf) - 1;)
462 if (i > 1 && buf[i - 1] == '!') {
463 /* The user selected to override the valididy of that
467 err = gpgme_get_key2 (context, buf, &key, 0);
469 key->uids->validity = GPGME_VALIDITY_FULL;
473 err = gpgme_get_key2 (context, buf, &key, 0);
476 p_realloc(&rset, rset_n + 1);
477 rset[rset_n++] = key;
480 mutt_error (_("error adding recipient `%s': %s\n"),
481 buf, gpgme_strerror (err));
489 /* NULL terminate. */
490 p_realloc(&rset, rset_n + 1);
491 rset[rset_n++] = NULL;
494 gpgme_release (context);
500 /* Make sure that the correct signer is set. Returns 0 on success. */
501 static int set_signer (gpgme_ctx_t ctx, int for_smime)
503 char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
506 gpgme_key_t key, key2;
508 if (!signid || !*signid)
511 listctx = create_gpgme_context (for_smime);
512 err = gpgme_op_keylist_start (listctx, signid, 1);
514 err = gpgme_op_keylist_next (listctx, &key);
516 gpgme_release (listctx);
517 mutt_error (_("secret key `%s' not found: %s\n"),
518 signid, gpgme_strerror (err));
521 err = gpgme_op_keylist_next (listctx, &key2);
523 gpgme_key_unref(key);
524 gpgme_key_unref(key2);
525 gpgme_release (listctx);
526 mutt_error (_("ambiguous specification of secret key `%s'\n"), signid);
529 gpgme_op_keylist_end (listctx);
530 gpgme_release (listctx);
532 gpgme_signers_clear (ctx);
533 err = gpgme_signers_add (ctx, key);
534 gpgme_key_unref(key);
536 mutt_error (_("error setting secret key `%s': %s\n"),
537 signid, gpgme_strerror (err));
544 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
545 and return an allocated filename to a temporary file containing the
546 enciphered text. With USE_SMIME set to true, the smime backend is
547 used. With COMBINED_SIGNED a PGP message is signed and
548 encrypted. Returns NULL in case of error */
549 static char *encrypt_gpgme_object (gpgme_data_t plaintext, gpgme_key_t * rset,
550 int use_smime, int combined_signed)
554 gpgme_data_t ciphertext;
557 ctx = create_gpgme_context (use_smime);
559 gpgme_set_armor (ctx, 1);
561 ciphertext = create_gpgme_data ();
563 if (combined_signed) {
564 if (set_signer (ctx, use_smime)) {
565 gpgme_data_release (ciphertext);
569 err = gpgme_op_encrypt_sign (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
570 plaintext, ciphertext);
573 err = gpgme_op_encrypt (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
574 plaintext, ciphertext);
575 mutt_need_hard_redraw ();
577 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
578 gpgme_data_release (ciphertext);
585 outfile = data_object_to_tempfile (ciphertext, NULL);
586 gpgme_data_release (ciphertext);
590 /* Find the "micalg" parameter from the last Gpgme operation on
591 context CTX. It is expected that this operation was a sign
592 operation. Return the algorithm name as a C string in buffer BUF
593 which must have been allocated by the caller with size BUFLEN.
594 Returns 0 on success or -1 in case of an error. The return string
595 is truncted to BUFLEN - 1. */
596 static int get_micalg (gpgme_ctx_t ctx, char *buf, ssize_t buflen)
598 gpgme_sign_result_t result = NULL;
599 const char *algorithm_name = NULL;
605 result = gpgme_op_sign_result (ctx);
607 algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
608 if (algorithm_name) {
609 m_strcpy(buf, buflen, algorithm_name);
613 return *buf ? 0 : -1;
616 static void print_time (time_t t, STATE * s)
620 setlocale (LC_TIME, "");
621 #ifdef HAVE_LANGINFO_D_T_FMT
622 strftime (p, sizeof (p), nl_langinfo (D_T_FMT), localtime (&t));
624 strftime (p, sizeof (p), "%c", localtime (&t));
626 setlocale (LC_TIME, "C");
627 state_attach_puts (p, s);
630 /* Implementation of `sign_message'. */
632 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
633 USE_SMIME is passed as true. Returns the new body or NULL on
635 static BODY *sign_message(BODY * a, int use_smime)
642 gpgme_data_t message, signature;
644 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
646 message = body_to_data_object(a, 1);
649 signature = create_gpgme_data ();
651 ctx = create_gpgme_context (use_smime);
653 gpgme_set_armor (ctx, 1);
655 if (set_signer (ctx, use_smime)) {
656 gpgme_data_release (signature);
661 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
662 mutt_need_hard_redraw ();
663 gpgme_data_release (message);
665 gpgme_data_release (signature);
667 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
671 sigfile = data_object_to_tempfile (signature, NULL);
672 gpgme_data_release (signature);
679 t->type = TYPEMULTIPART;
680 t->subtype = m_strdup("signed");
681 t->encoding = ENC7BIT;
683 t->disposition = DISPINLINE;
685 parameter_set_boundary(&t->parameter);
686 parameter_setval(&t->parameter, "protocol",
687 use_smime ? "application/pkcs7-signature"
688 : "application/pgp-signature");
689 /* Get the micalg from gpgme. Old gpgme versions don't support this
690 for S/MIME so we assume sha-1 in this case. */
691 if (!get_micalg (ctx, buf, sizeof buf))
692 parameter_setval(&t->parameter, "micalg", buf);
694 parameter_setval(&t->parameter, "micalg", "sha1");
700 t->parts->next = body_new();
702 t->type = TYPEAPPLICATION;
704 t->subtype = m_strdup("pkcs7-signature");
705 parameter_setval(&t->parameter, "name", "smime.p7s");
706 t->encoding = ENCBASE64;
708 t->disposition = DISPATTACH;
709 t->d_filename = m_strdup("smime.p7s");
712 t->subtype = m_strdup("pgp-signature");
714 t->disposition = DISPINLINE;
715 t->encoding = ENC7BIT;
717 t->filename = sigfile;
718 t->unlink = 1; /* ok to remove this file after sending. */
723 /* Encrypt the mail body A to all keys given as space separated keyids
724 or fingerprints in KEYLIST and return the encrypted body. */
725 static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
727 char *outfile = NULL;
729 gpgme_key_t *rset = NULL;
730 gpgme_data_t plaintext;
732 rset = create_recipient_set (keylist, GPGME_PROTOCOL_OpenPGP);
738 plaintext = body_to_data_object(a, 0);
744 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
745 gpgme_data_release (plaintext);
751 t->type = TYPEMULTIPART;
752 t->subtype = m_strdup("encrypted");
753 t->encoding = ENC7BIT;
755 t->disposition = DISPINLINE;
757 parameter_set_boundary(&t->parameter);
758 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
760 t->parts = body_new();
761 t->parts->type = TYPEAPPLICATION;
762 t->parts->subtype = m_strdup("pgp-encrypted");
763 t->parts->encoding = ENC7BIT;
765 t->parts->next = body_new();
766 t->parts->next->type = TYPEAPPLICATION;
767 t->parts->next->subtype = m_strdup("octet-stream");
768 t->parts->next->encoding = ENC7BIT;
769 t->parts->next->filename = outfile;
770 t->parts->next->use_disp = 1;
771 t->parts->next->disposition = DISPINLINE;
772 t->parts->next->unlink = 1; /* delete after sending the message */
773 t->parts->next->d_filename = m_strdup("msg.asc");
778 /* Encrypt the mail body A to all keys given as space separated
779 fingerprints in KEYLIST and return the S/MIME encrypted body. */
780 static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
782 char *outfile = NULL;
784 gpgme_key_t *rset = NULL;
785 gpgme_data_t plaintext;
787 rset = create_recipient_set (keylist, GPGME_PROTOCOL_CMS);
791 plaintext = body_to_data_object(a, 0);
797 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
798 gpgme_data_release (plaintext);
804 t->type = TYPEAPPLICATION;
805 t->subtype = m_strdup("pkcs7-mime");
806 parameter_setval(&t->parameter, "name", "smime.p7m");
807 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
808 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
810 t->disposition = DISPATTACH;
811 t->d_filename = m_strdup("smime.p7m");
812 t->filename = outfile;
813 t->unlink = 1; /*delete after sending the message */
820 /* Display the common attributes of the signature summary SUM.
821 Return 1 if there is is a severe warning.
823 static int show_sig_summary (unsigned long sum,
824 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
829 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
830 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
834 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
835 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
838 state_attach_puts (_("Warning: The key used to create the "
839 "signature expired at: "), s);
841 state_attach_puts ("\n", s);
844 state_attach_puts (_("Warning: At least one certification key "
845 "has expired\n"), s);
848 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
849 gpgme_verify_result_t result;
850 gpgme_signature_t sig;
853 result = gpgme_op_verify_result (ctx);
855 for (sig = result->signatures, i = 0; sig && (i < idx);
856 sig = sig->next, i++);
858 state_attach_puts (_("Warning: The signature expired at: "), s);
859 print_time (sig ? sig->exp_timestamp : 0, s);
860 state_attach_puts ("\n", s);
863 if ((sum & GPGME_SIGSUM_KEY_MISSING))
864 state_attach_puts (_("Can't verify due to a missing "
865 "key or certificate\n"), s);
867 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
868 state_attach_puts (_("The CRL is not available\n"), s);
872 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
873 state_attach_puts (_("Available CRL is too old\n"), s);
877 if ((sum & GPGME_SIGSUM_BAD_POLICY))
878 state_attach_puts (_("A policy requirement was not met\n"), s);
880 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
881 const char *t0 = NULL, *t1 = NULL;
882 gpgme_verify_result_t result;
883 gpgme_signature_t sig;
886 state_attach_puts (_("A system error occurred"), s);
888 /* Try to figure out some more detailed system error information. */
889 result = gpgme_op_verify_result (ctx);
890 for (sig = result->signatures, i = 0; sig && (i < idx);
891 sig = sig->next, i++);
894 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
898 state_attach_puts (": ", s);
900 state_attach_puts (t0, s);
901 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
903 state_attach_puts (",", s);
904 state_attach_puts (t1, s);
907 state_attach_puts ("\n", s);
914 static void show_fingerprint (gpgme_key_t key, STATE * state)
919 const char *prefix = _("Fingerprint: ");
924 s = key->subkeys ? key->subkeys->fpr : NULL;
927 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
929 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
930 buf = p_new(char, bufsize);
931 m_strcpy(buf, bufsize, prefix);
932 p = buf + m_strlen(buf);
933 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
934 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
945 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
948 *p++ = is_pgp ? ' ' : ':';
949 if (is_pgp && i == 7)
954 /* just in case print remaining odd digits */
959 state_attach_puts (buf, state);
963 /* Show the valididy of a key used for one signature. */
964 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
966 gpgme_verify_result_t result = NULL;
967 gpgme_signature_t sig = NULL;
968 const char *txt = NULL;
970 result = gpgme_op_verify_result (ctx);
972 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
974 switch (sig ? sig->validity : 0) {
975 case GPGME_VALIDITY_UNKNOWN:
976 txt = _("WARNING: We have NO indication whether "
977 "the key belongs to the person named " "as shown above\n");
979 case GPGME_VALIDITY_UNDEFINED:
981 case GPGME_VALIDITY_NEVER:
982 txt = _("WARNING: The key does NOT BELONG to "
983 "the person named as shown above\n");
985 case GPGME_VALIDITY_MARGINAL:
986 txt = _("WARNING: It is NOT certain that the key "
987 "belongs to the person named as shown above\n");
989 case GPGME_VALIDITY_FULL:
990 case GPGME_VALIDITY_ULTIMATE:
995 state_attach_puts (txt, s);
998 /* Show information about one signature. This fucntion is called with
999 the context CTX of a sucessful verification operation and the
1000 enumerator IDX which should start at 0 and incremete for each
1003 Return values are: 0 for normal procession, 1 for a bad signature,
1004 2 for a signature with a warning or -1 for no more signature. */
1005 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
1008 const char *fpr, *uid;
1009 gpgme_key_t key = NULL;
1010 int i, anybad = 0, anywarn = 0;
1012 gpgme_user_id_t uids = NULL;
1013 gpgme_verify_result_t result;
1014 gpgme_signature_t sig;
1015 gpgme_error_t err = GPG_ERR_NO_ERROR;
1017 result = gpgme_op_verify_result (ctx);
1019 /* FIXME: this code should use a static variable and remember
1020 the current position in the list of signatures, IMHO.
1023 for (i = 0, sig = result->signatures; sig && (i < idx);
1024 i++, sig = sig->next);
1026 return -1; /* Signature not found. */
1028 if (signature_key) {
1029 gpgme_key_unref(signature_key);
1030 signature_key = NULL;
1033 created = sig->timestamp;
1037 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1040 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1042 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1044 signature_key = key;
1047 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1048 error. Do it here to avoid a double free. */
1052 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1054 state_attach_puts (_("Error getting key information: "), s);
1055 state_attach_puts (gpg_strerror (err), s);
1056 state_attach_puts ("\n", s);
1059 else if ((sum & GPGME_SIGSUM_GREEN)) {
1060 state_attach_puts (_("Good signature from: "), s);
1061 state_attach_puts (uid, s);
1062 state_attach_puts ("\n", s);
1063 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1065 /* Skip primary UID. */
1069 state_attach_puts (_(" aka: "), s);
1070 state_attach_puts (uids->uid, s);
1071 state_attach_puts ("\n", s);
1073 state_attach_puts (_(" created: "), s);
1074 print_time (created, s);
1075 state_attach_puts ("\n", s);
1076 if (show_sig_summary (sum, ctx, key, idx, s))
1078 show_one_sig_validity (ctx, idx, s);
1080 else if ((sum & GPGME_SIGSUM_RED)) {
1081 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1082 state_attach_puts (uid, s);
1083 state_attach_puts ("\n", s);
1084 show_sig_summary (sum, ctx, key, idx, s);
1086 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1087 signature, so we display what a PGP user expects: The name,
1088 fingerprint and the key validity (which is neither fully or
1090 state_attach_puts (_("Good signature from: "), s);
1091 state_attach_puts (uid, s);
1092 state_attach_puts ("\n", s);
1093 state_attach_puts (_(" created: "), s);
1094 print_time (created, s);
1095 state_attach_puts ("\n", s);
1096 show_one_sig_validity (ctx, idx, s);
1097 show_fingerprint (key, s);
1098 if (show_sig_summary (sum, ctx, key, idx, s))
1101 else { /* can't decide (yellow) */
1103 state_attach_puts (_("Error checking signature"), s);
1104 state_attach_puts ("\n", s);
1105 show_sig_summary (sum, ctx, key, idx, s);
1108 if (key != signature_key)
1109 gpgme_key_unref(key);
1112 return anybad ? 1 : anywarn ? 2 : 0;
1115 /* Do the actual verification step. With IS_SMIME set to true we
1116 assume S/MIME (surprise!) */
1117 static int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
1123 gpgme_data_t signature, message;
1125 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1129 /* We need to tell gpgme about the encoding because the backend can't
1130 auto-detect plain base-64 encoding which is used by S/MIME. */
1132 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1134 err = gpgme_data_new_from_stream(&message, fp);
1136 gpgme_data_release (signature);
1137 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1140 ctx = create_gpgme_context (is_smime);
1142 /* Note: We don't need a current time output because GPGME avoids
1143 such an attack by separating the meta information from the
1145 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1147 err = gpgme_op_verify (ctx, signature, message, NULL);
1148 mutt_need_hard_redraw ();
1152 snprintf (buf, sizeof (buf) - 1,
1153 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1154 state_attach_puts (buf, s);
1156 else { /* Verification succeeded, see what the result is. */
1160 if (signature_key) {
1161 gpgme_key_unref(signature_key);
1162 signature_key = NULL;
1165 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1176 gpgme_verify_result_t result;
1177 gpgme_sig_notation_t notation;
1178 gpgme_signature_t sig;
1180 result = gpgme_op_verify_result (ctx);
1182 for (sig = result->signatures; sig; sig = sig->next) {
1183 if (sig->notations) {
1184 state_attach_puts ("*** Begin Notation (signature by: ", s);
1185 state_attach_puts (sig->fpr, s);
1186 state_attach_puts (") ***\n", s);
1187 for (notation = sig->notations; notation; notation = notation->next)
1189 if (notation->name) {
1190 state_attach_puts (notation->name, s);
1191 state_attach_puts ("=", s);
1193 if (notation->value) {
1194 state_attach_puts (notation->value, s);
1195 if (!(*notation->value
1196 && (notation->value[m_strlen(notation->value) - 1] ==
1198 state_attach_puts ("\n", s);
1201 state_attach_puts ("*** End Notation ***\n", s);
1207 gpgme_release (ctx);
1209 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1211 return badsig ? 1 : anywarn ? 2 : 0;
1214 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1215 IS_SMIME) with body A described further by state S. Write
1216 plaintext out to file FPOUT and return a new body. For PGP returns
1217 a flag in R_IS_SIGNED to indicate whether this is a combined
1218 encrypted and signed message, for S/MIME it returns true when it is
1219 not a encrypted but a signed message. */
1221 decrypt_part(BODY *a, STATE *s, FILE *fpout, int is_smime, int *r_is_signed)
1227 gpgme_data_t ciphertext, plaintext;
1228 int maybe_signed = 0;
1235 ctx = create_gpgme_context (is_smime);
1238 /* Make a data object from the body, create context etc. */
1239 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1242 plaintext = create_gpgme_data ();
1244 /* Do the decryption or the verification in case of the S/MIME hack. */
1245 if ((!is_smime) || maybe_signed) {
1247 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1248 else if (maybe_signed)
1249 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1252 /* Check wether signatures have been verified. */
1253 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1255 if (verify_result->signatures)
1260 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1261 gpgme_data_release (ciphertext);
1263 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1264 /* Check whether this might be a signed message despite what
1265 the mime header told us. Retry then. gpgsm returns the
1266 error information "unsupported Algorithm '?'" but gpgme
1267 will not store this unknown algorithm, thus we test that
1268 it has not been set. */
1269 gpgme_decrypt_result_t result;
1271 result = gpgme_op_decrypt_result (ctx);
1272 if (!result->unsupported_algorithm) {
1274 gpgme_data_release (plaintext);
1278 mutt_need_hard_redraw ();
1279 if ((s->flags & M_DISPLAY)) {
1282 snprintf (buf, sizeof (buf) - 1,
1283 _("[-- Error: decryption failed: %s --]\n\n"),
1284 gpgme_strerror (err));
1285 state_attach_puts (buf, s);
1287 gpgme_data_release (plaintext);
1288 gpgme_release (ctx);
1291 mutt_need_hard_redraw ();
1293 /* Read the output from GPGME, and make sure to change CRLF to LF,
1294 otherwise read_mime_header has a hard time parsing the message. */
1295 if (data_object_to_stream (plaintext, fpout)) {
1296 gpgme_data_release (plaintext);
1297 gpgme_release (ctx);
1300 gpgme_data_release (plaintext);
1302 a->is_signed_data = 0;
1308 a->is_signed_data = 1;
1310 *r_is_signed = -1; /* A signature exists. */
1312 if ((s->flags & M_DISPLAY))
1313 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1314 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1320 if (!anybad && idx && r_is_signed && *r_is_signed)
1321 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1323 if ((s->flags & M_DISPLAY))
1324 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1326 gpgme_release (ctx);
1331 tattach = mutt_read_mime_header (fpout, 0);
1334 * Need to set the length of this body part.
1336 fstat (fileno (fpout), &info);
1337 tattach->length = info.st_size - tattach->offset;
1339 tattach->warnsig = anywarn;
1341 /* See if we need to recurse on this MIME part. */
1342 mutt_parse_part (fpout, tattach);
1348 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1349 the stream in CUR and FPOUT. Returns 0 on success. */
1350 int crypt_pgp_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1352 char tempfile[_POSIX_PATH_MAX];
1354 BODY *first_part = b;
1357 first_part->goodsig = 0;
1358 first_part->warnsig = 0;
1360 if (!mutt_is_multipart_encrypted (b))
1363 if (!b->parts || !b->parts->next)
1370 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1372 mutt_perror (_("Can't create temporary file"));
1377 *cur = decrypt_part (b, &s, *fpout, 0, &is_signed);
1380 first_part->goodsig = 1;
1382 return *cur ? 0 : -1;
1386 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1387 the stream in CUR and FPOUT. Returns 0 on success. */
1388 int crypt_smime_decrypt_mime(FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
1390 char tempfile[_POSIX_PATH_MAX];
1394 long saved_b_offset;
1395 ssize_t saved_b_length;
1398 if (!mutt_is_application_smime (b))
1404 /* Decode the body - we need to pass binary CMS to the
1405 backend. The backend allows for Base64 encoded data but it does
1406 not allow for QP which I have seen in some messages. So better
1408 saved_b_type = b->type;
1409 saved_b_offset = b->offset;
1410 saved_b_length = b->length;
1413 fseeko (s.fpin, b->offset, 0);
1414 tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1416 mutt_perror (_("Can't create temporary file"));
1419 mutt_unlink (tempfile);
1422 mutt_decode_attachment (b, &s);
1424 b->length = ftello (s.fpout);
1431 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1433 mutt_perror (_("Can't create temporary file"));
1436 mutt_unlink (tempfile);
1438 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1440 (*cur)->goodsig = is_signed > 0;
1441 b->type = saved_b_type;
1442 b->length = saved_b_length;
1443 b->offset = saved_b_offset;
1446 if (*cur && !is_signed && !(*cur)->parts
1447 && mutt_is_application_smime (*cur)) {
1448 /* Assume that this is a opaque signed s/mime message. This is
1449 an ugly way of doing it but we have anyway a problem with
1450 arbitrary encoded S/MIME messages: Only the outer part may be
1451 encrypted. The entire mime parsing should be revamped,
1452 probably by keeping the temportary files so that we don't
1453 need to decrypt them all the time. Inner parts of an
1454 encrypted part can then pint into this file and tehre won't
1455 never be a need to decrypt again. This needs a partial
1456 rewrite of the MIME engine. */
1460 saved_b_type = bb->type;
1461 saved_b_offset = bb->offset;
1462 saved_b_length = bb->length;
1465 fseeko (s.fpin, bb->offset, 0);
1466 tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1468 mutt_perror (_("Can't create temporary file"));
1471 mutt_unlink (tempfile);
1474 mutt_decode_attachment (bb, &s);
1476 bb->length = ftello (s.fpout);
1484 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1486 mutt_perror (_("Can't create temporary file"));
1489 mutt_unlink (tempfile);
1491 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1493 tmp_b->goodsig = is_signed > 0;
1494 bb->type = saved_b_type;
1495 bb->length = saved_b_length;
1496 bb->offset = saved_b_offset;
1499 body_list_wipe(cur);
1502 return *cur ? 0 : -1;
1507 pgp_check_traditional_one_body(FILE *fp, BODY *b, int tagged_only)
1509 char tempfile[_POSIX_PATH_MAX];
1510 char buf[HUGE_STRING];
1517 if (b->type != TYPETEXT)
1520 if (tagged_only && !b->tagged)
1523 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1524 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1529 if ((tfp = fopen(tempfile, "r")) == NULL) {
1534 while (fgets (buf, sizeof (buf), tfp)) {
1535 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1536 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1538 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1548 /* fix the content type */
1550 parameter_setval(&b->parameter, "format", "fixed");
1551 parameter_setval(&b->parameter, "x-action",
1552 enc ? "pgp-encrypted" : "pgp-signed");
1556 int crypt_pgp_check_traditional(FILE *fp, BODY *b, int tagged_only)
1561 for (; b; b = b->next) {
1562 if (is_multipart (b))
1563 rv = (crypt_pgp_check_traditional (fp, b->parts, tagged_only) || rv);
1564 else if (b->type == TYPETEXT) {
1565 if ((r = mutt_is_application_pgp (b)))
1568 rv = (pgp_check_traditional_one_body (fp, b, tagged_only) || rv);
1575 Copy a clearsigned message, and strip the signature and PGP's
1578 XXX - charset handling: We assume that it is safe to do
1579 character set decoding first, dash decoding second here, while
1580 we do it the other way around in the main handler.
1582 (Note that we aren't worse than Outlook & Cie in this, and also
1583 note that we can successfully handle anything produced by any
1584 existing versions of mutt.) */
1585 static void copy_clearsigned(gpgme_data_t data, STATE * s, char *charset)
1587 char buf[HUGE_STRING];
1588 short complete, armor_header;
1593 fname = data_object_to_tempfile (data, &fp);
1599 fc = fgetconv_open (fp, charset, MCharset.charset, M_ICONV_HOOK_FROM);
1601 for (complete = 1, armor_header = 1;
1602 fgetconvs (buf, sizeof (buf), fc) != NULL;
1603 complete = strchr (buf, '\n') != NULL) {
1606 state_puts (buf, s);
1610 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1620 state_puts (s->prefix, s);
1622 if (buf[0] == '-' && buf[1] == ' ')
1623 state_puts (buf + 2, s);
1625 state_puts (buf, s);
1628 fgetconv_close (&fc);
1632 /* Support for classic_application/pgp */
1633 int crypt_pgp_application_pgp_handler(BODY *m, STATE *s)
1635 int needpass = -1, pgp_keyblock = 0;
1639 off_t last_pos, offset;
1640 char buf[HUGE_STRING];
1641 FILE *pgpout = NULL;
1643 gpgme_error_t err = 0;
1644 gpgme_data_t armored_data = NULL;
1646 short maybe_goodsig = 1;
1647 short have_any_sigs = 0;
1649 char body_charset[STRING]; /* Only used for clearsigned messages. */
1651 /* For clearsigned messages we won't be able to get a character set
1652 but we know that this may only be text thus we assume Latin-1
1654 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1655 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1657 fseeko (s->fpin, m->offset, 0);
1658 last_pos = m->offset;
1660 for (bytes = m->length; bytes > 0;) {
1661 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1664 offset = ftello (s->fpin);
1665 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1668 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1670 start_pos = last_pos;
1672 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1674 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1678 else if (!option (OPTDONTHANDLEPGPKEYS) &&
1679 !m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1684 /* XXX - we may wish to recode here */
1686 state_puts (s->prefix, s);
1687 state_puts (buf, s);
1691 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1693 /* Copy PGP material to an data container */
1694 armored_data = create_gpgme_data ();
1695 gpgme_data_write (armored_data, buf, m_strlen(buf));
1696 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1697 offset = ftello (s->fpin);
1698 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1701 gpgme_data_write (armored_data, buf, m_strlen(buf));
1703 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1705 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1706 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1711 /* Invoke PGP if needed */
1712 if (!clearsign || (s->flags & M_VERIFY)) {
1713 unsigned int sig_stat = 0;
1714 gpgme_data_t plaintext;
1717 plaintext = create_gpgme_data ();
1718 ctx = create_gpgme_context (0);
1721 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1723 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1724 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1725 /* Decrypt verify can't handle signed only messages. */
1726 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1727 ? gpgme_error_from_errno (errno) : 0;
1728 /* Must release plaintext so that we supply an
1729 uninitialized object. */
1730 gpgme_data_release (plaintext);
1731 plaintext = create_gpgme_data ();
1732 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1739 snprintf (errbuf, sizeof (errbuf) - 1,
1740 _("Error: decryption/verification failed: %s\n"),
1741 gpgme_strerror (err));
1742 state_attach_puts (errbuf, s);
1744 else { /* Decryption/Verification succeeded */
1748 /* Check wether signatures have been verified. */
1749 gpgme_verify_result_t verify_result;
1751 verify_result = gpgme_op_verify_result (ctx);
1752 if (verify_result->signatures)
1758 if ((s->flags & M_DISPLAY) && sig_stat) {
1763 state_attach_puts (_("[-- Begin signature "
1764 "information --]\n"), s);
1767 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1776 state_attach_puts (_("[-- End signature "
1777 "information --]\n\n"), s);
1780 tmpfname = data_object_to_tempfile (plaintext, &pgpout);
1783 state_attach_puts (_("Error: copy data failed\n"), s);
1787 p_delete(&tmpfname);
1790 gpgme_release (ctx);
1794 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1795 * outputs utf-8 cleartext. This may not always be true, but it
1796 * seems to be a reasonable guess.
1799 if (s->flags & M_DISPLAY) {
1801 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1802 else if (pgp_keyblock)
1803 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1805 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1809 copy_clearsigned (armored_data, s, body_charset);
1816 fc = fgetconv_open (pgpout, "utf-8", MCharset.charset, 0);
1817 while ((c = fgetconv (fc)) != EOF) {
1819 if (c == '\n' && s->prefix)
1820 state_puts (s->prefix, s);
1822 fgetconv_close (&fc);
1825 if (s->flags & M_DISPLAY) {
1826 state_putc ('\n', s);
1828 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1829 else if (pgp_keyblock)
1830 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1832 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1840 /* XXX - we may wish to recode here */
1842 state_puts (s->prefix, s);
1843 state_puts (buf, s);
1847 m->goodsig = (maybe_goodsig && have_any_sigs);
1849 if (needpass == -1) {
1850 state_attach_puts (_("[-- Error: could not find beginning"
1851 " of PGP message! --]\n\n"), s);
1857 /* MIME handler for pgp/mime encrypted messages. */
1858 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1860 char tempfile[_POSIX_PATH_MAX];
1863 BODY *orig_body = a;
1868 if (!a || a->type != TYPEAPPLICATION || !a->subtype
1869 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
1870 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
1871 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
1872 if (s->flags & M_DISPLAY)
1873 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
1878 /* Move forward to the application/pgp-encrypted body. */
1881 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1883 if (s->flags & M_DISPLAY)
1884 state_attach_puts (_("[-- Error: could not create temporary file! "
1889 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
1891 tattach->goodsig = is_signed > 0;
1893 if (s->flags & M_DISPLAY)
1894 state_attach_puts (is_signed ?
1896 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
1897 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
1900 FILE *savefp = s->fpin;
1903 rc = mutt_body_handler (tattach, s);
1908 * if a multipart/signed is the _only_ sub-part of a
1909 * multipart/encrypted, cache signature verification
1912 if (mutt_is_multipart_signed (tattach) && !tattach->next)
1913 orig_body->goodsig |= tattach->goodsig;
1915 if (s->flags & M_DISPLAY) {
1916 state_puts ("\n", s);
1917 state_attach_puts (is_signed ?
1919 ("[-- End of PGP/MIME signed and encrypted data --]\n")
1920 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
1923 body_list_wipe(&tattach);
1927 mutt_unlink (tempfile);
1931 /* Support for application/smime */
1932 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
1934 char tempfile[_POSIX_PATH_MAX];
1941 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1943 if (s->flags & M_DISPLAY)
1944 state_attach_puts (_("[-- Error: could not create temporary file! "
1949 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
1951 tattach->goodsig = is_signed > 0;
1953 if (s->flags & M_DISPLAY)
1954 state_attach_puts (is_signed ?
1955 _("[-- The following data is S/MIME signed --]\n\n") :
1956 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
1959 FILE *savefp = s->fpin;
1962 rc = mutt_body_handler (tattach, s);
1967 * if a multipart/signed is the _only_ sub-part of a
1968 * multipart/encrypted, cache signature verification
1971 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
1972 if (!(a->goodsig = tattach->goodsig))
1973 a->warnsig = tattach->warnsig;
1975 else if (tattach->goodsig) {
1977 a->warnsig = tattach->warnsig;
1980 if (s->flags & M_DISPLAY) {
1981 state_puts ("\n", s);
1982 state_attach_puts (is_signed ?
1983 _("[-- End of S/MIME signed data --]\n") :
1984 _("[-- End of S/MIME encrypted data --]\n"), s);
1987 body_list_wipe(&tattach);
1991 mutt_unlink (tempfile);
1997 * Format an entry on the CRYPT key selection menu.
2000 * %k key id %K key id of the principal key
2002 * %a algorithm %A algorithm of the princ. key
2003 * %l length %L length of the princ. key
2004 * %f flags %F flags of the princ. key
2005 * %c capabilities %C capabilities of the princ. key
2006 * %t trust/validity of the key-uid association
2008 * %[...] date of key using strftime(3)
2012 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
2013 const char *src, const char *prefix,
2014 const char *ifstr, const char *elstr,
2015 anytype data, format_flag flags)
2018 crypt_entry_t *entry;
2021 int optional = (flags & M_FORMAT_OPTIONAL);
2022 const char *s = NULL;
2028 /* if (isupper ((unsigned char) op)) */
2031 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
2034 switch (ascii_tolower (op)) {
2038 char buf2[STRING], *p;
2054 while (len > 0 && *cp != ']') {
2063 break; /* not enough space */
2073 if (do_locales && Locale)
2074 setlocale (LC_TIME, Locale);
2079 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2080 tt = key->kobj->subkeys->timestamp;
2082 tm = localtime (&tt);
2084 strftime (buf2, sizeof (buf2), dest, tm);
2087 setlocale (LC_TIME, "C");
2089 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2090 snprintf (dest, destlen, fmt, buf2);
2097 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2098 snprintf (dest, destlen, fmt, entry->num);
2103 /* fixme: we need a way to distinguish between main and subkeys.
2104 Store the idx in entry? */
2105 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2106 snprintf (dest, destlen, fmt, crypt_keyid (key));
2111 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2112 snprintf (dest, destlen, fmt, key->uid);
2117 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2118 if (key->kobj->subkeys)
2119 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2122 snprintf (dest, destlen, fmt, s);
2127 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2128 if (key->kobj->subkeys)
2129 val = key->kobj->subkeys->length;
2132 snprintf (dest, destlen, fmt, val);
2137 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2138 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2140 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2145 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2146 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2148 else if (!(kflags & (KEYFLAG_ABILITIES)))
2152 if ((kflags & KEYFLAG_ISX509))
2155 gpgme_user_id_t uid = NULL;
2158 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2159 i++, uid = uid->next);
2161 switch (uid->validity) {
2162 case GPGME_VALIDITY_UNDEFINED:
2165 case GPGME_VALIDITY_NEVER:
2168 case GPGME_VALIDITY_MARGINAL:
2171 case GPGME_VALIDITY_FULL:
2174 case GPGME_VALIDITY_ULTIMATE:
2177 case GPGME_VALIDITY_UNKNOWN:
2183 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2184 snprintf (dest, destlen, fmt, s ? *s : 'B');
2187 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2188 snprintf (dest, destlen, fmt,
2189 gpgme_get_protocol_name (key->kobj->protocol));
2196 if (flags & M_FORMAT_OPTIONAL)
2197 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2198 mutt_attach_fmt, data, 0);
2202 /* Used by the display fucntion to format a line. */
2203 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2205 cryptkey_t **cryptkey_table = (cryptkey_t **) menu->data;
2206 crypt_entry_t entry;
2208 entry.key = cryptkey_table[num];
2209 entry.num = num + 1;
2211 m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
2212 option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
2215 /* Compare two addresses and the keyid to be used for sorting. */
2216 static int _crypt_compare_address (const void *a, const void *b)
2218 cryptkey_t **s = (cryptkey_t **) a;
2219 cryptkey_t **t = (cryptkey_t **) b;
2222 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2225 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2228 static int crypt_compare_address (const void *a, const void *b)
2230 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2231 : _crypt_compare_address (a, b));
2235 /* Compare two key IDs and the addresses to be used for sorting. */
2236 static int _crypt_compare_keyid (const void *a, const void *b)
2238 cryptkey_t **s = (cryptkey_t **) a;
2239 cryptkey_t **t = (cryptkey_t **) b;
2242 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2245 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2248 static int crypt_compare_keyid (const void *a, const void *b)
2250 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2251 : _crypt_compare_keyid (a, b));
2254 /* Compare 2 creation dates and the addresses. For sorting. */
2255 static int _crypt_compare_date (const void *a, const void *b)
2257 cryptkey_t **s = (cryptkey_t **) a;
2258 cryptkey_t **t = (cryptkey_t **) b;
2259 unsigned long ts = 0, tt = 0;
2261 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2262 ts = (*s)->kobj->subkeys->timestamp;
2263 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2264 tt = (*t)->kobj->subkeys->timestamp;
2271 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2274 static int crypt_compare_date (const void *a, const void *b)
2276 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2277 : _crypt_compare_date (a, b));
2280 /* Compare two trust values, the key length, the creation dates. the
2281 addresses and the key IDs. For sorting. */
2282 static int _crypt_compare_trust (const void *a, const void *b)
2284 cryptkey_t **s = (cryptkey_t **) a;
2285 cryptkey_t **t = (cryptkey_t **) b;
2286 unsigned long ts = 0, tt = 0;
2289 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2290 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2293 if ((*s)->kobj->uids)
2294 ts = (*s)->kobj->uids->validity;
2295 if ((*t)->kobj->uids)
2296 tt = (*t)->kobj->uids->validity;
2297 if ((r = (tt - ts)))
2300 if ((*s)->kobj->subkeys)
2301 ts = (*s)->kobj->subkeys->length;
2302 if ((*t)->kobj->subkeys)
2303 tt = (*t)->kobj->subkeys->length;
2307 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2308 ts = (*s)->kobj->subkeys->timestamp;
2309 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2310 tt = (*t)->kobj->subkeys->timestamp;
2316 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2318 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2321 static int crypt_compare_trust (const void *a, const void *b)
2323 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2324 : _crypt_compare_trust (a, b));
2327 /* Print the X.500 Distinguished Name part KEY from the array of parts
2329 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2333 for (; dn->key; dn++) {
2334 if (!m_strcmp(dn->key, key)) {
2337 print_utf8 (fp, dn->value, m_strlen(dn->value));
2344 /* Print all parts of a DN in a standard sequence. */
2345 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2347 const char *stdpart[] = {
2348 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2350 int any = 0, any2 = 0, i;
2352 for (i = 0; stdpart[i]; i++) {
2355 any = print_dn_part (fp, dn, stdpart[i]);
2357 /* now print the rest without any specific ordering */
2358 for (; dn->key; dn++) {
2359 for (i = 0; stdpart[i]; i++) {
2360 if (!m_strcmp(dn->key, stdpart[i]))
2368 any = print_dn_part (fp, dn, dn->key);
2377 /* Parse an RDN; this is a helper to parse_dn(). */
2378 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2379 const unsigned char *string)
2381 const unsigned char *s, *s1;
2385 /* parse attributeType */
2386 for (s = string + 1; *s && *s != '='; s++);
2388 return NULL; /* error */
2391 return NULL; /* empty key */
2392 array->key = p_dupstr(string, n );
2393 p = (unsigned char *) array->key;
2396 if (*string == '#') { /* hexstring */
2398 for (s = string; hexval(*s) >= 0; s++)
2402 return NULL; /* empty or odd number of digits */
2404 p = p_new(unsigned char, n + 1);
2405 array->value = (char *) p;
2406 for (s1 = string; n; s1 += 2, n--)
2407 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2410 else { /* regular v3 quoted string */
2411 for (n = 0, s = string; *s; s++) {
2412 if (*s == '\\') { /* pair */
2414 if (*s == ',' || *s == '=' || *s == '+'
2415 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2416 || *s == '\\' || *s == '\"' || *s == ' ')
2418 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2423 return NULL; /* invalid escape sequence */
2425 else if (*s == '\"')
2426 return NULL; /* invalid encoding */
2427 else if (*s == ',' || *s == '=' || *s == '+'
2428 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2434 p = p_new(unsigned char, n + 1);
2435 array->value = (char *) p;
2436 for (s = string; n; s++, n--) {
2439 if (hexval(*s) >= 0) {
2440 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2455 /* Parse a DN and return an array-ized one. This is not a validating
2456 parser and it does not support any old-stylish syntax; gpgme is
2457 expected to return only rfc2253 compatible strings. */
2458 static struct dn_array_s *parse_dn (const unsigned char *string)
2460 struct dn_array_s *array;
2461 ssize_t arrayidx, arraysize;
2464 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2465 array = p_new(struct dn_array_s, arraysize + 1);
2468 while (*string == ' ')
2472 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2473 struct dn_array_s *a2;
2476 a2 = p_new(struct dn_array_s, arraysize + 1);
2477 for (i = 0; i < arrayidx; i++) {
2478 a2[i].key = array[i].key;
2479 a2[i].value = array[i].value;
2484 array[arrayidx].key = NULL;
2485 array[arrayidx].value = NULL;
2486 string = parse_dn_part (array + arrayidx, string);
2490 while (*string == ' ')
2492 if (*string && *string != ',' && *string != ';' && *string != '+')
2493 goto failure; /* invalid delimiter */
2497 array[arrayidx].key = NULL;
2498 array[arrayidx].value = NULL;
2502 for (i = 0; i < arrayidx; i++) {
2503 p_delete(&array[i].key);
2504 p_delete(&array[i].value);
2511 /* Print a nice representation of the USERID and make sure it is
2512 displayed in a proper way, which does mean to reorder some parts
2513 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2514 functions. It is utf-8 encoded. */
2515 static void parse_and_print_user_id(FILE * fp, const char *userid)
2520 if (*userid == '<') {
2521 s = strchr (userid + 1, '>');
2523 print_utf8 (fp, userid + 1, s - userid - 1);
2525 else if (*userid == '(')
2526 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2527 else if (*userid & ~127 || __m_strdigits[(int)*userid] == 255)
2528 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2530 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2533 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2535 print_dn_parts (fp, dn);
2536 for (i = 0; dn[i].key; i++) {
2537 p_delete(&dn[i].key);
2538 p_delete(&dn[i].value);
2546 KEY_CAP_CAN_ENCRYPT,
2551 static unsigned int key_check_cap(gpgme_key_t key, key_cap_t cap)
2553 gpgme_subkey_t subkey = NULL;
2554 unsigned int ret = 0;
2557 case KEY_CAP_CAN_ENCRYPT:
2558 if (!(ret = key->can_encrypt))
2559 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2560 if ((ret = subkey->can_encrypt))
2563 case KEY_CAP_CAN_SIGN:
2564 if (!(ret = key->can_sign))
2565 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2566 if ((ret = subkey->can_sign))
2569 case KEY_CAP_CAN_CERTIFY:
2570 if (!(ret = key->can_certify))
2571 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2572 if ((ret = subkey->can_certify))
2581 /* Print verbose information about a key or certificate to FP. */
2582 static void print_key_info (gpgme_key_t key, FILE * fp)
2585 const char *s = NULL, *s2 = NULL;
2588 char shortbuf[STRING];
2589 unsigned long aval = 0;
2593 gpgme_user_id_t uid = NULL;
2596 setlocale (LC_TIME, Locale);
2598 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2600 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2605 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2608 fputs (_("[Invalid]"), fp);
2612 print_utf8 (fp, s, m_strlen(s));
2614 parse_and_print_user_id (fp, s);
2618 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2619 tt = key->subkeys->timestamp;
2621 tm = localtime (&tt);
2622 #ifdef HAVE_LANGINFO_D_T_FMT
2623 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2625 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2627 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2630 if (key->subkeys && (key->subkeys->expires > 0)) {
2631 tt = key->subkeys->expires;
2633 tm = localtime (&tt);
2634 #ifdef HAVE_LANGINFO_D_T_FMT
2635 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2637 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2639 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2643 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2647 s2 = is_pgp ? "PGP" : "X.509";
2650 aval = key->subkeys->length;
2652 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2654 fprintf (fp, _("Key Usage .: "));
2657 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2658 fprintf (fp, "%s%s", delim, _("encryption"));
2661 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2662 fprintf (fp, "%s%s", delim, _("signing"));
2665 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2666 fprintf (fp, "%s%s", delim, _("certification"));
2672 s = key->subkeys->fpr;
2673 fputs (_("Fingerprint: "), fp);
2674 if (is_pgp && m_strlen(s) == 40) {
2675 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2680 putc (is_pgp ? ' ' : ':', fp);
2681 if (is_pgp && i == 4)
2686 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2689 putc (is_pgp ? ' ' : ':', fp);
2690 if (is_pgp && i == 7)
2694 fprintf (fp, "%s\n", s);
2697 if (key->issuer_serial) {
2698 s = key->issuer_serial;
2700 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2703 if (key->issuer_name) {
2704 s = key->issuer_name;
2706 fprintf (fp, _("Issued By .: "));
2707 parse_and_print_user_id (fp, s);
2712 /* For PGP we list all subkeys. */
2714 gpgme_subkey_t subkey = NULL;
2716 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2720 if (m_strlen(s) == 16)
2721 s += 8; /* display only the short keyID */
2722 fprintf (fp, _("Subkey ....: 0x%s"), s);
2723 if (subkey->revoked) {
2725 fputs (_("[Revoked]"), fp);
2727 if (subkey->invalid) {
2729 fputs (_("[Invalid]"), fp);
2731 if (subkey->expired) {
2733 fputs (_("[Expired]"), fp);
2735 if (subkey->disabled) {
2737 fputs (_("[Disabled]"), fp);
2741 if (subkey->timestamp > 0) {
2742 tt = subkey->timestamp;
2744 tm = localtime (&tt);
2745 #ifdef HAVE_LANGINFO_D_T_FMT
2746 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2748 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2750 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2753 if (subkey->expires > 0) {
2754 tt = subkey->expires;
2756 tm = localtime (&tt);
2757 #ifdef HAVE_LANGINFO_D_T_FMT
2758 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2760 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2762 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2766 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2771 aval = subkey->length;
2775 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2777 fprintf (fp, _("Key Usage .: "));
2780 if (subkey->can_encrypt) {
2781 fprintf (fp, "%s%s", delim, _("encryption"));
2784 if (subkey->can_sign) {
2785 fprintf (fp, "%s%s", delim, _("signing"));
2788 if (subkey->can_certify) {
2789 fprintf (fp, "%s%s", delim, _("certification"));
2797 setlocale (LC_TIME, "C");
2801 /* Show detailed information about the selected key */
2802 static void verify_key (cryptkey_t * key)
2805 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2807 gpgme_ctx_t listctx = NULL;
2809 gpgme_key_t k = NULL;
2812 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2814 mutt_perror (_("Can't create temporary file"));
2817 mutt_message _("Collecting data...");
2819 print_key_info (key->kobj, fp);
2821 err = gpgme_new (&listctx);
2823 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2824 gpgme_strerror (err));
2827 if ((key->flags & KEYFLAG_ISX509))
2828 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2832 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2834 err = gpgme_op_keylist_start (listctx, s, 0);
2838 err = gpgme_op_keylist_next (listctx, &k);
2840 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2843 gpgme_op_keylist_end (listctx);
2845 print_key_info (k, fp);
2848 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2855 gpgme_release (listctx);
2857 mutt_clear_error ();
2858 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2859 mutt_do_pager (cmd, tempfile, 0, NULL);
2862 /* Implementation of `findkeys'. */
2864 static void add_hints(string_array *arr, const char *s)
2870 int l = strcspn(s, " ,.:\"()<>\n");
2871 string_array_append(arr, p_dupstr(s, l));
2873 s += strspn(s, " ,.:\"()<>\n");
2877 /* Return a list of keys which are candidates for the selection. */
2879 get_candidates(string_array *hints, unsigned int app, int secret)
2881 cryptkey_t *res = NULL, **kend = &res;
2886 if (hints->len <= 0)
2888 string_array_append(hints, NULL);
2889 ctx = create_gpgme_context(0);
2891 if ((app & APPLICATION_PGP)) {
2892 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2895 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2896 gpgme_strerror(err));
2901 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2902 gpgme_user_id_t uid = NULL;
2903 unsigned int flags = 0;
2906 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2907 flags |= KEYFLAG_CANENCRYPT;
2908 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2909 flags |= KEYFLAG_CANSIGN;
2911 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2912 cryptkey_t *k = p_new(cryptkey_t, 1);
2921 if (gpg_err_code(err) != GPG_ERR_EOF)
2922 mutt_error(_("gpgme_op_keylist_next failed: %s"), gpgme_strerror(err));
2923 gpgme_op_keylist_end(ctx);
2926 if ((app & APPLICATION_SMIME)) {
2927 gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
2928 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2931 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2932 gpgme_strerror(err));
2937 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2938 gpgme_user_id_t uid = NULL;
2939 unsigned int flags = KEYFLAG_ISX509;
2942 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2943 flags |= KEYFLAG_CANENCRYPT;
2944 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2945 flags |= KEYFLAG_CANSIGN;
2947 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2948 cryptkey_t *k = p_new(cryptkey_t, 1);
2957 if (gpg_err_code(err) != GPG_ERR_EOF)
2958 mutt_error(_("gpgme_op_keylist_next failed: %s"),
2959 gpgme_strerror(err));
2960 gpgme_op_keylist_end(ctx);
2967 /* Display a menu to select a key from the array KEYS. FORCED_VALID
2968 will be set to true on return if the user did override the the
2970 static cryptkey_t *crypt_select_key (cryptkey_t * keys,
2971 address_t * p, const char *s,
2972 unsigned int app, int *forced_valid)
2975 cryptkey_t **cryptkey_table;
2978 char helpstr[STRING], buf[LONG_STRING];
2980 int (*f) (const void *, const void *);
2981 int menu_to_use = 0;
2986 /* build the key table */
2988 cryptkey_table = NULL;
2989 for (k = keys; k; k = k->next) {
2990 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
2997 p_realloc(&cryptkey_table, keymax);
3000 cryptkey_table[i++] = k;
3003 if (!i && unusable) {
3004 mutt_error _("All matching keys are marked expired/revoked.");
3010 switch (PgpSortKeys & SORT_MASK) {
3012 f = crypt_compare_date;
3015 f = crypt_compare_keyid;
3018 f = crypt_compare_address;
3022 f = crypt_compare_trust;
3025 qsort (cryptkey_table, i, sizeof (cryptkey_t *), f);
3027 if (app & APPLICATION_PGP)
3028 menu_to_use = MENU_KEY_SELECT_PGP;
3029 else if (app & APPLICATION_SMIME)
3030 menu_to_use = MENU_KEY_SELECT_SMIME;
3033 mutt_make_help (buf, sizeof (buf), _("Exit "), menu_to_use, OP_EXIT);
3034 m_strcat(helpstr, sizeof(helpstr), buf);
3035 mutt_make_help (buf, sizeof (buf), _("Select "), menu_to_use,
3036 OP_GENERIC_SELECT_ENTRY);
3037 m_strcat(helpstr, sizeof(helpstr), buf);
3038 mutt_make_help (buf, sizeof (buf), _("Check key "),
3039 menu_to_use, OP_VERIFY_KEY);
3040 m_strcat(helpstr, sizeof(helpstr), buf);
3041 mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
3042 m_strcat(helpstr, sizeof(helpstr), buf);
3044 menu = mutt_new_menu ();
3046 menu->make_entry = crypt_entry;
3047 menu->menu = menu_to_use;
3048 menu->help = helpstr;
3049 menu->data = cryptkey_table;
3054 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3055 ts = _("PGP and S/MIME keys matching");
3056 else if ((app & APPLICATION_PGP))
3057 ts = _("PGP keys matching");
3058 else if ((app & APPLICATION_SMIME))
3059 ts = _("S/MIME keys matching");
3061 ts = _("keys matching");
3064 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3066 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3070 mutt_clear_error ();
3074 switch (mutt_menuLoop (menu)) {
3076 verify_key (cryptkey_table[menu->current]);
3077 menu->redraw = REDRAW_FULL;
3081 mutt_message ("%s", cryptkey_table[menu->current]->uid);
3084 case OP_GENERIC_SELECT_ENTRY:
3085 /* FIXME make error reporting more verbose - this should be
3086 easy because gpgme provides more information */
3087 if (option (OPTPGPCHECKTRUST)) {
3088 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE ) {
3089 mutt_error(_("This key can't be used: "
3090 "expired/disabled/revoked."));
3095 if (option (OPTPGPCHECKTRUST) &&
3096 ((cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3097 || !crypt_id_is_strong (cryptkey_table[menu->current]))) {
3099 char buff[LONG_STRING];
3101 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3102 s = N_("ID is expired/disabled/revoked.");
3104 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3105 gpgme_user_id_t uid = NULL;
3110 uid = cryptkey_table[menu->current]->kobj->uids;
3111 for (j = 0; (j < cryptkey_table[menu->current]->idx) && uid;
3112 j++, uid = uid->next);
3114 val = uid->validity;
3117 case GPGME_VALIDITY_UNKNOWN:
3118 case GPGME_VALIDITY_UNDEFINED:
3119 warn_s = N_("ID has undefined validity.");
3121 case GPGME_VALIDITY_NEVER:
3122 warn_s = N_("ID is not valid.");
3124 case GPGME_VALIDITY_MARGINAL:
3125 warn_s = N_("ID is only marginally valid.");
3127 case GPGME_VALIDITY_FULL:
3128 case GPGME_VALIDITY_ULTIMATE:
3132 snprintf (buff, sizeof (buff),
3133 _("%s Do you really want to use the key?"), _(warn_s));
3135 if (mutt_yesorno (buff, 0) != 1) {
3136 mutt_clear_error ();
3143 k = cryptkey_dup(cryptkey_table[menu->current]);
3154 mutt_menuDestroy (&menu);
3155 p_delete(&cryptkey_table);
3157 set_option (OPTNEEDREDRAW);
3162 static cryptkey_t *crypt_getkeybyaddr (address_t * a, short abilities,
3163 unsigned int app, int *forced_valid)
3170 int this_key_has_strong;
3171 int this_key_has_weak;
3172 int this_key_has_invalid;
3175 cryptkey_t *keys, *k;
3176 cryptkey_t *the_valid_key = NULL;
3177 cryptkey_t *matches = NULL;
3178 cryptkey_t **matches_endp = &matches;
3184 string_array_init(&hints);
3185 add_hints(&hints, a->mailbox);
3186 add_hints(&hints, a->personal);
3188 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3189 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3190 string_array_wipe(&hints);
3196 for (k = keys; k; k = k->next) {
3197 if (abilities && !(k->flags & abilities)) {
3201 this_key_has_weak = 0; /* weak but valid match */
3202 this_key_has_invalid = 0; /* invalid match */
3203 this_key_has_strong = 0; /* strong and valid match */
3204 match = 0; /* any match */
3206 r = rfc822_parse_adrlist (NULL, k->uid);
3207 for (p = r; p; p = p->next) {
3208 int validity = crypt_id_matches_addr (a, p, k);
3210 if (validity & CRYPT_KV_MATCH) /* something matches */
3213 /* is this key a strong candidate? */
3214 if ((validity & CRYPT_KV_VALID)
3215 && (validity & CRYPT_KV_STRONGID)
3216 && (validity & CRYPT_KV_ADDR)) {
3217 if (the_valid_key && the_valid_key != k)
3220 this_key_has_strong = 1;
3222 else if ((validity & CRYPT_KV_MATCH)
3223 && !(validity & CRYPT_KV_VALID))
3224 this_key_has_invalid = 1;
3225 else if ((validity & CRYPT_KV_MATCH)
3226 && (!(validity & CRYPT_KV_STRONGID)
3227 || !(validity & CRYPT_KV_ADDR)))
3228 this_key_has_weak = 1;
3230 address_list_wipe(&r);
3235 if (!this_key_has_strong && this_key_has_invalid)
3237 if (!this_key_has_strong && this_key_has_weak)
3240 *matches_endp = tmp = cryptkey_dup(k);
3241 matches_endp = &tmp->next;
3242 the_valid_key = tmp;
3245 key_list_wipe(&keys);
3248 if (the_valid_key && !multi && !weak
3249 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3251 * There was precisely one strong match on a valid ID, there
3252 * were no valid keys with weak matches, and we aren't
3253 * interested in seeing invalid keys.
3255 * Proceed without asking the user.
3257 k = cryptkey_dup(the_valid_key);
3260 * Else: Ask the user.
3262 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3264 key_list_wipe(&matches);
3273 static cryptkey_t *crypt_getkeybystr (const char *p, short abilities,
3274 unsigned int app, int *forced_valid)
3277 cryptkey_t *matches = NULL;
3278 cryptkey_t **matches_endp = &matches;
3282 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3288 string_array_init(&hints);
3289 add_hints(&hints, p);
3290 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3291 string_array_wipe(&hints);
3297 for (k = keys; k; k = k->next) {
3298 const char *s = crypt_keyid(k);
3300 if (abilities && !(k->flags & abilities))
3305 if (!*p || !m_strcasecmp(p, s)
3306 || (!m_strncasecmp(p, "0x", 2) && !m_strcasecmp(p + 2, s))
3307 || m_stristr(k->uid, p))
3311 *matches_endp = tmp = cryptkey_dup(k);
3312 matches_endp = &tmp->next;
3315 key_list_wipe(&keys);
3318 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3319 key_list_wipe(&matches);
3326 /* Display TAG as a prompt to ask for a key.
3327 * ABILITIES describe the required key abilities (sign, encrypt) and APP the
3328 * type of the requested key; ether S/MIME or PGP.
3329 * Return a copy of the key or NULL if not found. */
3331 crypt_ask_for_key(const char *tag, int abilities, int app, int *forced_valid)
3338 forced_valid = &dummy;
3344 if (mutt_get_field(tag, resp, sizeof(resp), M_CLEAR) != 0)
3347 if (m_strisempty(resp))
3350 if ((key = crypt_getkeybystr(resp, abilities, app, forced_valid)))
3357 /* This routine attempts to find the keyids of the recipients of a
3358 message. It returns NULL if any of the keys can not be found. */
3359 static char *find_keys(ENVELOPE *env, unsigned int app)
3361 address_t *lst = NULL, *addr;
3362 buffer_t *keylist = buffer_new();
3365 address_t **last = &lst;
3366 *last = address_list_dup(env->to);
3367 last = address_list_last(last);
3368 *last = address_list_dup(env->cc);
3369 last = address_list_last(last);
3370 *last = address_list_dup(env->bcc);
3372 rfc822_qualify(lst, mutt_fqdn(1));
3373 address_list_uniq(lst);
3376 while ((addr = address_list_pop(&lst))) {
3378 int forced_valid = 0;
3380 cryptkey_t *key = NULL;
3382 if ((keyID = mutt_crypt_hook(addr))) {
3385 snprintf(buf, sizeof(buf), _("Use keyID = \"%s\" for %s?"), keyID,
3387 r = mutt_yesorno(buf, M_YES);
3390 address_list_wipe(&lst);
3391 address_list_wipe(&addr);
3392 buffer_delete(&keylist);
3398 /* check for e-mail address */
3399 if (strchr(keyID, '@') && (a = rfc822_parse_adrlist(NULL, keyID))) {
3400 rfc822_qualify(a, mutt_fqdn(1));
3401 address_list_wipe(&addr);
3404 key = crypt_getkeybystr(keyID, KEYFLAG_CANENCRYPT, app,
3411 key = crypt_getkeybyaddr(addr, KEYFLAG_CANENCRYPT, app, &forced_valid);
3414 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), addr->mailbox);
3415 key = crypt_ask_for_key(buf, KEYFLAG_CANENCRYPT, app,
3418 address_list_wipe(&lst);
3419 address_list_wipe(&addr);
3420 buffer_delete(&keylist);
3426 buffer_addch(keylist, ' ');
3427 buffer_addstr(keylist, "0x");
3428 buffer_addstr(keylist, crypt_fpr(key));
3430 buffer_addch(keylist, '!');
3432 key_list_wipe(&key);
3433 address_list_wipe(&addr);
3436 address_list_wipe(&lst);
3437 return buffer_unwrap(&keylist);
3440 int crypt_get_keys(HEADER *msg, char **keylist)
3442 /* Do a quick check to make sure that we can find all of the encryption
3443 * keys if the user has requested this service.
3448 if (msg->security & ENCRYPT) {
3449 if (msg->security & APPLICATION_PGP) {
3450 set_option(OPTPGPCHECKTRUST);
3451 *keylist = find_keys(msg->env, APPLICATION_PGP);
3452 unset_option(OPTPGPCHECKTRUST);
3457 if (msg->security & APPLICATION_SMIME) {
3458 *keylist = find_keys(msg->env, APPLICATION_SMIME);
3468 int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
3474 if (msg->security & APPLICATION_SMIME)
3476 if (msg->security & APPLICATION_PGP)
3480 ? mutt_multi_choice(_("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3482 : mutt_multi_choice(_("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3486 case 1: /* (e)ncrypt */
3487 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3488 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3491 case 2: /* (s)ign */
3492 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3493 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3496 case 3: /* sign (a)s */
3497 p = crypt_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN,
3498 is_smime ? APPLICATION_SMIME : APPLICATION_PGP,
3501 snprintf(buf, sizeof(buf), "0x%s", crypt_keyid(p));
3502 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs, buf);
3504 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3506 *redraw = REDRAW_FULL;
3509 case 4: /* (b)oth */
3511 msg->security = SMIMEENCRYPT | SMIMESIGN;
3513 msg->security = PGPENCRYPT | PGPSIGN;
3517 case 5: /* (p)gp or s/(m)ime */
3518 is_smime = !is_smime;
3521 case 6: /* (c)lear */
3522 return msg->security = 0;
3526 msg->security &= ~APPLICATION_PGP;
3527 msg->security |= APPLICATION_SMIME;
3529 msg->security &= ~APPLICATION_SMIME;
3530 msg->security |= APPLICATION_PGP;
3533 return msg->security;
3536 int crypt_smime_verify_sender(HEADER *h)
3538 address_t *sender = NULL;
3539 unsigned int ret = 1;
3542 h->env->from = mutt_expand_aliases(h->env->from);
3543 sender = h->env->from;
3544 } else if (h->env->sender) {
3545 h->env->sender = mutt_expand_aliases (h->env->sender);
3546 sender = h->env->sender;
3550 mutt_any_key_to_continue ("Failed to figure out sender");
3554 if (signature_key) {
3555 gpgme_key_t key = signature_key;
3556 gpgme_user_id_t uid = NULL;
3557 int sender_length = 0;
3560 sender_length = m_strlen(sender->mailbox);
3561 for (uid = key->uids; uid && ret; uid = uid->next) {
3562 uid_length = m_strlen(uid->email);
3563 if (1 && (uid->email[0] == '<')
3564 && (uid->email[uid_length - 1] == '>')
3565 && (uid_length == sender_length + 2)
3566 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3570 mutt_any_key_to_continue ("Failed to verify sender");
3574 if (signature_key) {
3575 gpgme_key_unref(signature_key);
3576 signature_key = NULL;
3581 static void crypt_invoke_import(FILE *stream, int smime)
3583 gpgme_ctx_t ctx = create_gpgme_context(smime);
3587 err = gpgme_data_new_from_stream(&data, stream);
3589 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3594 err = gpgme_op_import(ctx, data);
3596 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3597 gpgme_data_release(data);
3602 gpgme_data_release(data);
3607 static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
3610 FILE *tmpfp = tmpfile();
3612 if (tmpfp == NULL) {
3613 mutt_perror (_("Can't create temporary file"));
3620 mutt_body_handler(top, &s);
3623 crypt_invoke_import(tmpfp, 0);
3627 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3630 set_option (OPTDONTHANDLEPGPKEYS);
3632 for (; top; top = top->next) {
3633 if (!tag || top->tagged)
3634 pgp_extract_keys_from_attachment (fp, top);
3640 unset_option (OPTDONTHANDLEPGPKEYS);
3643 void crypt_invoke_message (int type)
3645 if (type & APPLICATION_PGP) {
3646 mutt_message _("Invoking PGP...");
3648 else if (type & APPLICATION_SMIME) {
3649 mutt_message _("Invoking S/MIME...");
3653 int mutt_protect (HEADER * msg, char *keylist)
3655 BODY *pbody = NULL, *tmp_pbody = NULL;
3656 BODY *tmp_smime_pbody = NULL;
3657 BODY *tmp_pgp_pbody = NULL;
3658 int flags = msg->security;
3663 tmp_smime_pbody = msg->content;
3664 tmp_pgp_pbody = msg->content;
3666 if (msg->security & SIGN) {
3667 if (msg->security & APPLICATION_SMIME) {
3668 if (!(tmp_pbody = sign_message(msg->content, 1)))
3670 pbody = tmp_smime_pbody = tmp_pbody;
3673 if ((msg->security & APPLICATION_PGP)
3674 && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
3675 if (!(tmp_pbody = sign_message(msg->content, 0)))
3679 pbody = tmp_pgp_pbody = tmp_pbody;
3682 if ((msg->security & APPLICATION_SMIME)
3683 && (msg->security & APPLICATION_PGP)) {
3684 /* here comes the draft ;-) */
3689 if (msg->security & ENCRYPT) {
3690 if ((msg->security & APPLICATION_SMIME)) {
3691 if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
3693 /* signed ? free it! */
3696 /* free tmp_body if messages was signed AND encrypted ... */
3697 if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
3698 /* detatch and dont't delete msg->content,
3699 which tmp_smime_pbody->parts after signing. */
3700 tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
3701 msg->content->next = NULL;
3702 body_list_wipe(&tmp_smime_pbody);
3707 if ((msg->security & APPLICATION_PGP)) {
3708 if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
3711 /* did we perform a retainable signature? */
3712 if (flags != msg->security) {
3713 /* remove the outer multipart layer */
3714 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3715 /* get rid of the signature */
3716 body_list_wipe(&tmp_pgp_pbody->next);
3722 /* destroy temporary signature envelope when doing retainable
3726 if (flags != msg->security) {
3727 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3728 body_list_wipe(&tmp_pgp_pbody->next);
3734 msg->content = pbody;
3740 int crypt_query (BODY * m)
3747 if (m->type == TYPEAPPLICATION) {
3748 t |= mutt_is_application_pgp (m);
3750 t |= mutt_is_application_smime (m);
3751 if (t && m->goodsig)
3756 else if (m->type == TYPETEXT) {
3757 t |= mutt_is_application_pgp (m);
3758 if (t && m->goodsig)
3762 if (m->type == TYPEMULTIPART) {
3763 t |= mutt_is_multipart_encrypted (m);
3764 t |= mutt_is_multipart_signed (m);
3766 if (t && m->goodsig)
3770 if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
3774 u = m->parts ? ~0 : 0; /* Bits set in all parts */
3775 w = 0; /* Bits set in any part */
3777 for (p = m->parts; p; p = p->next) {
3778 v = crypt_query (p);
3782 t |= u | (w & ~GOODSIGN);
3784 if ((w & GOODSIGN) && !(u & GOODSIGN))
3792 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
3798 fseeko (s->fpin, a->hdr_offset, 0);
3799 bytes = a->length + a->offset - a->hdr_offset;
3802 if ((c = fgetc (s->fpin)) == EOF)
3810 if (c == '\n' && !hadcr)
3819 static void extract_keys_aux(FILE *fpout, HEADER *h)
3821 mutt_parse_mime_message (Context, h);
3824 if (h->security & APPLICATION_PGP) {
3825 mutt_copy_message(fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
3828 mutt_endwin (_("Trying to extract PGP keys...\n"));
3831 if (h->security & APPLICATION_SMIME) {
3832 if (h->security & ENCRYPT)
3833 mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
3834 | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
3836 mutt_copy_message(fpout, Context, h, 0, 0);
3839 mutt_message (_("Trying to extract S/MIME certificates...\n"));
3843 crypt_invoke_import(fpout, h->security & APPLICATION_SMIME);
3846 void crypt_extract_keys_from_messages(HEADER * h)
3848 FILE *tmpfp = tmpfile();
3850 mutt_error(_("Could not create temporary file"));
3854 set_option(OPTDONTHANDLEPGPKEYS);
3857 for (i = 0; i < Context->vcount; i++) {
3858 if (!Context->hdrs[Context->v2r[i]]->tagged)
3860 extract_keys_aux(tmpfp, Context->hdrs[Context->v2r[i]]);
3863 extract_keys_aux(tmpfp, h);
3865 unset_option(OPTDONTHANDLEPGPKEYS);
3869 mutt_any_key_to_continue(NULL);
3874 static void crypt_fetch_signatures (BODY ***signatures, BODY * a, int *n)
3876 for (; a; a = a->next) {
3877 if (a->type == TYPEMULTIPART)
3878 crypt_fetch_signatures (signatures, a->parts, n);
3881 p_realloc(signatures, *n + 6);
3883 (*signatures)[(*n)++] = a;
3890 * This routine verifies a "multipart/signed" body.
3893 int mutt_signed_handler (BODY * a, STATE * s)
3895 unsigned major, minor;
3897 int rc, i, goodsig = 1, sigcnt = 0;
3900 protocol = parameter_getval(a->parameter, "protocol");
3903 switch (mime_which_token(protocol, -1)) {
3904 case MIME_APPLICATION_PGP_SIGNATURE:
3905 major = TYPEAPPLICATION;
3906 minor = MIME_PGP_SIGNATURE;
3908 case MIME_APPLICATION_X_PKCS7_SIGNATURE:
3909 major = TYPEAPPLICATION;
3910 minor = MIME_X_PKCS7_SIGNATURE;
3912 case MIME_APPLICATION_PKCS7_SIGNATURE:
3913 major = TYPEAPPLICATION;
3914 minor = MIME_PKCS7_SIGNATURE;
3916 case MIME_MULTIPART_MIXED:
3917 major = TYPEMULTIPART;
3922 state_printf(s, _("[-- Error: "
3923 "Unknown multipart/signed protocol %s! --]\n\n"),
3925 return mutt_body_handler (a, s);
3928 /* consistency check */
3929 if (!(a && a->next && a->next->type == major &&
3930 mime_which_token(a->next->subtype, -1) == minor))
3932 state_attach_puts(_("[-- Error: "
3933 "Inconsistent multipart/signed structure! --]\n\n"),
3935 return mutt_body_handler (a, s);
3938 if (s->flags & M_DISPLAY) {
3941 crypt_fetch_signatures (&sigs, a->next, &sigcnt);
3943 FILE *tmpfp = tmpfile();
3946 mutt_error(_("Could not create temporary file"));
3948 crypt_write_signed(a, s, tmpfp);
3950 for (i = 0; i < sigcnt; i++) {
3951 if (sigs[i]->type == TYPEAPPLICATION) {
3954 switch ((subtype = mime_which_token(sigs[i]->subtype, -1))) {
3955 case MIME_PGP_SIGNATURE:
3956 case MIME_X_PKCS7_SIGNATURE:
3957 case MIME_PKCS7_SIGNATURE:
3958 if (crypt_verify_one(sigs[i], s, tmpfp, subtype != MIME_PGP_SIGNATURE) != 0)
3969 state_printf(s, _("[-- Warning: "
3970 "We can't verify %s/%s signatures. --]\n\n"),
3971 TYPE (sigs[i]), sigs[i]->subtype);
3975 b->goodsig = goodsig;
3976 b->badsig = !goodsig;
3978 /* Now display the signed body */
3979 state_attach_puts(_("[-- The following data is signed --]\n\n"), s);
3983 state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"),
3988 rc = mutt_body_handler (a, s);
3990 if (s->flags & M_DISPLAY && sigcnt)
3991 state_attach_puts (_("\n[-- End of signed data --]\n"), s);