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>
29 #include "recvattach.h"
32 /* Values used for comparing addresses. */
33 #define CRYPT_KV_VALID 1
34 #define CRYPT_KV_ADDR 2
35 #define CRYPT_KV_STRING 4
36 #define CRYPT_KV_STRONGID 8
37 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
44 /* We work based on user IDs, getting from a user ID to the key is
45 check and does not need any memory (gpgme uses reference counting). */
46 typedef struct cryptkey_t {
47 struct cryptkey_t *next;
48 int idx; /* and the user ID at this index */
49 int flags; /* global and per uid flags (for convenience) */
51 const char *uid; /* and for convenience point to this user ID */
54 DO_INIT(cryptkey_t, cryptkey);
55 static void cryptkey_wipe(cryptkey_t *key) {
56 gpgme_key_release(key->kobj);
58 DO_NEW(cryptkey_t, cryptkey);
59 DO_DELETE(cryptkey_t, cryptkey);
60 DO_SLIST(cryptkey_t, key, cryptkey_delete);
62 static cryptkey_t *cryptkey_dup(const cryptkey_t *k)
64 cryptkey_t *res = cryptkey_new();
67 gpgme_key_ref(k->kobj);
71 typedef struct crypt_entry {
76 static gpgme_key_t signature_key = NULL;
78 static void convert_to_7bit (BODY * a)
80 for (; a; a = a->next) {
81 int tok = mime_which_token(a->subtype, -1);
83 if (a->type == TYPEMULTIPART) {
84 a->encoding = ENC7BIT;
85 convert_to_7bit(a->parts);
86 } else if (a->type == TYPEMESSAGE && tok == MIME_DELIVERY_STATUS) {
87 if (a->encoding != ENC7BIT)
88 mutt_message_to_7bit(a, NULL);
89 } else if (a->encoding == ENC8BIT) {
90 a->encoding = ENCQUOTEDPRINTABLE;
91 } else if (a->encoding == ENCBINARY) {
92 a->encoding = ENCBASE64;
93 } else if (a->content && a->encoding != ENCBASE64
94 && (a->content->from || a->content->space))
96 a->encoding = ENCQUOTEDPRINTABLE;
101 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
102 FP. Convert the character set. */
103 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
107 tstr = p_dupstr(buf, len);
108 mutt_convert_string(&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
113 /* Return the keyID for the key K. Note that this string is valid as
114 long as K is valid */
115 static const char *crypt_keyid (cryptkey_t * k)
117 if (k->kobj && k->kobj->subkeys) {
118 const char *s = k->kobj->subkeys->keyid;
119 return m_strlen(s) == 16 ? s + 8 : s;
125 /* Return the hexstring fingerprint from the key K. */
126 static const char *crypt_fpr (cryptkey_t * k)
128 return k->kobj && k->kobj->subkeys ? k->kobj->subkeys->fpr : "";
131 /* Parse FLAGS and return a statically allocated(!) string with them. */
132 static char *crypt_key_abilities (int flags)
134 static char buff[3] = "es";
136 if (!(flags & KEYFLAG_CANENCRYPT))
138 else if (flags & KEYFLAG_PREFER_SIGNING)
141 if (!(flags & KEYFLAG_CANSIGN))
143 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
149 /* Parse FLAGS and return a character describing the most important flag. */
150 static char crypt_flags(int flags)
152 if (flags & KEYFLAG_REVOKED)
154 if (flags & KEYFLAG_EXPIRED)
156 if (flags & KEYFLAG_DISABLED)
158 if (flags & KEYFLAG_CRITICAL)
163 /* Return true whe validity of KEY is sufficient. */
164 static int crypt_id_is_strong (cryptkey_t * key)
169 if (key->flags & KEYFLAG_ISX509)
172 for (i = 0, uid = key->kobj->uids; uid; i++, uid = uid->next) {
174 return uid->validity == GPGME_VALIDITY_FULL
175 || uid->validity == GPGME_VALIDITY_ULTIMATE;
182 /* Return a bit vector describing how well the addresses ADDR and
183 U_ADDR match and whether KEY is valid. */
185 crypt_id_matches_addr(address_t *addr, address_t *u_addr, cryptkey_t *key)
189 if (!(key->flags & KEYFLAG_CANTUSE))
190 rv |= CRYPT_KV_VALID;
192 if (crypt_id_is_strong(key))
193 rv |= CRYPT_KV_STRONGID;
195 if (addr->mailbox && !m_strcasecmp(addr->mailbox, u_addr->mailbox))
198 if (addr->personal && m_strcasecmp(addr->personal, u_addr->personal))
199 rv |= CRYPT_KV_STRING;
205 /* Create a new gpgme context and return it. With FOR_SMIME set to
206 true, the protocol of the context is set to CMS. */
207 static gpgme_ctx_t create_gpgme_context(int for_smime)
212 err = gpgme_new (&ctx);
214 mutt_error(_("error creating gpgme context: %s\n"),
215 gpgme_strerror(err));
222 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
224 mutt_error(_("error enabling CMS protocol: %s\n"), gpgme_strerror(err));
231 /* Create a new gpgme data object. This is a wrapper to die on
233 static gpgme_data_t create_gpgme_data(void)
238 err = gpgme_data_new(&data);
240 mutt_error(_("error creating gpgme data object: %s\n"),
241 gpgme_strerror(err));
248 /* Create a new GPGME Data object from the mail body A. With CONVERT
249 passed as true, the lines are converted to CR,LF if required.
250 Return NULL on error or the gpgme_data_t object on success. */
251 static gpgme_data_t body_to_data_object(BODY *a, int convert)
257 if (!(fptmp = tmpfile())) {
258 mutt_perror (_("Can't create temporary file"));
262 mutt_write_mime_header(a, fptmp);
264 mutt_write_mime_body(a, fptmp);
271 data = create_gpgme_data();
273 while (fgets(buf + spare, sizeof(buf) - 1, fptmp)) {
274 int l = m_strlen(buf);
276 spare = buf[l - 1] != '\n';
277 if (!spare && (l <= 1 || buf[l - 2] != '\r')) {
281 gpgme_data_write(data, buf, l - spare);
286 gpgme_data_write(data, buf, 1);
287 gpgme_data_seek(data, 0, SEEK_SET);
292 err = gpgme_data_new_from_stream(&data, fptmp);
295 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
301 /* Create a GPGME data object from the stream FP but limit the object
302 to LENGTH bytes starting at OFFSET bytes from the beginning of the
304 static gpgme_data_t file_to_data_object(FILE *fp, long offset, long length)
309 err = gpgme_data_new_from_filepart(&data, NULL, fp, offset, length);
311 mutt_error(_("error allocating data object: %s\n"),
312 gpgme_strerror(err));
319 /* Write a GPGME data object to the stream FP. */
320 static int data_object_to_stream(gpgme_data_t data, FILE *fp)
326 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
327 ? gpgme_error_from_errno (errno) : 0);
329 mutt_error (_("error rewinding data object: %s\n"),
330 gpgme_strerror(err));
334 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
335 /* fixme: we are not really converting CRLF to LF but just
336 skipping CR. Doing it correctly needs a more complex logic */
337 for (p = buf; nread; p++, nread--) {
343 mutt_perror ("[tempfile]");
348 mutt_error(_("error reading data object: %s\n"), strerror(errno));
354 /* Copy a data object to a newly created temporay file and return that
355 filename. Caller must free. With RET_FP not NULL, don't close the
356 stream but return it there. */
357 static char *data_object_to_tempfile(gpgme_data_t data, FILE **ret_fp)
360 char tempfile[_POSIX_PATH_MAX];
364 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
366 mutt_perror (_("Can't create temporary file"));
370 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
371 ? gpgme_error_from_errno (errno) : 0);
375 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
376 if (fwrite (buf, nread, 1, fp) != 1) {
377 mutt_perror (_("Can't create temporary file"));
386 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
397 return m_strdup(tempfile);
401 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
403 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
409 if (!ctx || !r_key || !fpr)
410 return gpg_error (GPG_ERR_INV_VALUE);
412 if (strlen (fpr) < 8) /* We have at least a key ID. */
413 return gpg_error (GPG_ERR_INV_VALUE);
415 /* FIXME: We use our own context because we have to avoid the user's
416 I/O callback handlers. */
417 err = gpgme_new (&listctx);
420 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
421 err = gpgme_op_keylist_start (listctx, fpr, secret);
423 err = gpgme_op_keylist_next (listctx, r_key);
424 gpgme_release (listctx);
428 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
429 The keys must be space delimited. */
430 static gpgme_key_t *create_recipient_set(const char *s, int smime)
432 gpgme_ctx_t ctx = create_gpgme_context(smime);
433 gpgme_key_t *rset = NULL;
440 const char *p = m_strnextsp(s);
443 m_strncpy(buf, sizeof(buf), s, p - s);
444 if (p - s > 1 && p[-1] == '!') {
445 /* user wants to override the valididy of that key. */
447 buf[p - s - 1] = '\0';
448 err = gpgme_get_key2(ctx, buf, &key, 0);
450 key->uids->validity = GPGME_VALIDITY_FULL;
452 err = gpgme_get_key2(ctx, buf, &key, 0);
456 mutt_error(_("error adding recipient `%.*s': %s\n"),
457 (int)(p - s), s, gpgme_strerror(err));
462 p_realloc(&rset, rset_n + 1);
463 rset[rset_n++] = key;
468 /* NULL terminate. */
469 p_realloc(&rset, rset_n + 1);
470 rset[rset_n++] = NULL;
478 /* Make sure that the correct signer is set. Returns 0 on success. */
479 static int set_signer(gpgme_ctx_t ctx, int for_smime)
481 const char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
485 if (m_strisempty(signid))
488 err = gpgme_get_key(ctx, signid, &key, 1);
490 mutt_error(_("error getting secret key `%s': %s\n"), signid,
491 gpgme_strerror(err));
495 gpgme_signers_clear(ctx);
496 err = gpgme_signers_add(ctx, key);
497 gpgme_key_unref(key);
499 mutt_error(_("error setting secret key `%s': %s\n"), signid,
500 gpgme_strerror(err));
507 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
508 and return an allocated filename to a temporary file containing the
509 enciphered text. With USE_SMIME set to true, the smime backend is
510 used. With COMBINED_SIGNED a PGP message is signed and
511 encrypted. Returns NULL in case of error */
512 static char *encrypt_gpgme_object(gpgme_data_t plaintext, gpgme_key_t *rset,
513 int use_smime, int combined_signed)
517 gpgme_data_t ciphertext;
520 ctx = create_gpgme_context (use_smime);
522 gpgme_set_armor (ctx, 1);
524 ciphertext = create_gpgme_data ();
526 if (combined_signed) {
527 if (set_signer(ctx, use_smime)) {
528 gpgme_data_release(ciphertext);
532 err = gpgme_op_encrypt_sign(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
533 plaintext, ciphertext);
535 err = gpgme_op_encrypt(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
536 plaintext, ciphertext);
539 mutt_need_hard_redraw();
541 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
542 gpgme_data_release (ciphertext);
549 outfile = data_object_to_tempfile(ciphertext, NULL);
550 gpgme_data_release(ciphertext);
554 /* Find the "micalg" parameter from the last Gpgme operation on
555 context CTX. It is expected that this operation was a sign
556 operation. Return the algorithm name as a C string in buffer BUF
557 which must have been allocated by the caller with size BUFLEN.
558 Returns 0 on success or -1 in case of an error. The return string
559 is truncted to BUFLEN - 1. */
560 static int get_micalg(gpgme_ctx_t ctx, char *buf, ssize_t buflen)
562 gpgme_sign_result_t result = NULL;
563 const char *alg = NULL;
565 result = gpgme_op_sign_result(ctx);
566 if (result && result->signatures) {
567 alg = gpgme_hash_algo_name(result->signatures->hash_algo);
569 m_strcpy(buf, buflen, NONULL(alg));
574 static void print_time(time_t t, STATE *s)
578 setlocale(LC_TIME, "");
579 #ifdef HAVE_LANGINFO_D_T_FMT
580 strftime(p, sizeof(p), nl_langinfo(D_T_FMT), localtime(&t));
582 strftime(p, sizeof(p), "%c", localtime(&t));
584 setlocale(LC_TIME, "C");
585 state_attach_puts(p, s);
588 /* Implementation of `sign_message'. */
590 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
591 USE_SMIME is passed as true. Returns the new body or NULL on
593 static BODY *sign_message(BODY * a, int use_smime)
600 gpgme_data_t message, signature;
602 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
604 message = body_to_data_object(a, 1);
607 signature = create_gpgme_data ();
609 ctx = create_gpgme_context (use_smime);
611 gpgme_set_armor (ctx, 1);
613 if (set_signer (ctx, use_smime)) {
614 gpgme_data_release (signature);
619 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
620 mutt_need_hard_redraw ();
621 gpgme_data_release (message);
623 gpgme_data_release (signature);
625 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
629 sigfile = data_object_to_tempfile(signature, NULL);
630 gpgme_data_release (signature);
637 t->type = TYPEMULTIPART;
638 t->subtype = m_strdup("signed");
639 t->encoding = ENC7BIT;
641 t->disposition = DISPINLINE;
643 parameter_set_boundary(&t->parameter);
644 parameter_setval(&t->parameter, "protocol",
645 use_smime ? "application/pkcs7-signature"
646 : "application/pgp-signature");
647 /* Get the micalg from gpgme. Old gpgme versions don't support this
648 for S/MIME so we assume sha-1 in this case. */
649 if (!get_micalg (ctx, buf, sizeof buf))
650 parameter_setval(&t->parameter, "micalg", buf);
652 parameter_setval(&t->parameter, "micalg", "sha1");
658 t->parts->next = body_new();
660 t->type = TYPEAPPLICATION;
662 t->subtype = m_strdup("pkcs7-signature");
663 parameter_setval(&t->parameter, "name", "smime.p7s");
664 t->encoding = ENCBASE64;
666 t->disposition = DISPATTACH;
667 t->d_filename = m_strdup("smime.p7s");
670 t->subtype = m_strdup("pgp-signature");
672 t->disposition = DISPINLINE;
673 t->encoding = ENC7BIT;
675 t->filename = sigfile;
676 t->unlink = 1; /* ok to remove this file after sending. */
681 /* Encrypt the mail body A to all keys given as space separated keyids
682 or fingerprints in KEYLIST and return the encrypted body. */
683 static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
685 char *outfile = NULL;
687 gpgme_key_t *rset = NULL;
688 gpgme_data_t plaintext;
690 rset = create_recipient_set(keylist, 0);
696 plaintext = body_to_data_object(a, 0);
702 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
703 gpgme_data_release (plaintext);
709 t->type = TYPEMULTIPART;
710 t->subtype = m_strdup("encrypted");
711 t->encoding = ENC7BIT;
713 t->disposition = DISPINLINE;
715 parameter_set_boundary(&t->parameter);
716 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
718 t->parts = body_new();
719 t->parts->type = TYPEAPPLICATION;
720 t->parts->subtype = m_strdup("pgp-encrypted");
721 t->parts->encoding = ENC7BIT;
723 t->parts->next = body_new();
724 t->parts->next->type = TYPEAPPLICATION;
725 t->parts->next->subtype = m_strdup("octet-stream");
726 t->parts->next->encoding = ENC7BIT;
727 t->parts->next->filename = outfile;
728 t->parts->next->use_disp = 1;
729 t->parts->next->disposition = DISPINLINE;
730 t->parts->next->unlink = 1; /* delete after sending the message */
731 t->parts->next->d_filename = m_strdup("msg.asc");
736 /* Encrypt the mail body A to all keys given as space separated
737 fingerprints in KEYLIST and return the S/MIME encrypted body. */
738 static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
740 char *outfile = NULL;
742 gpgme_key_t *rset = NULL;
743 gpgme_data_t plaintext;
745 rset = create_recipient_set(keylist, 1);
749 plaintext = body_to_data_object(a, 0);
755 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
756 gpgme_data_release (plaintext);
762 t->type = TYPEAPPLICATION;
763 t->subtype = m_strdup("pkcs7-mime");
764 parameter_setval(&t->parameter, "name", "smime.p7m");
765 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
766 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
768 t->disposition = DISPATTACH;
769 t->d_filename = m_strdup("smime.p7m");
770 t->filename = outfile;
771 t->unlink = 1; /*delete after sending the message */
778 /* Display the common attributes of the signature summary SUM.
779 Return 1 if there is is a severe warning.
781 static int show_sig_summary (unsigned long sum,
782 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
787 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
788 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
792 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
793 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
796 state_attach_puts (_("Warning: The key used to create the "
797 "signature expired at: "), s);
799 state_attach_puts ("\n", s);
802 state_attach_puts (_("Warning: At least one certification key "
803 "has expired\n"), s);
806 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
807 gpgme_verify_result_t result;
808 gpgme_signature_t sig;
811 result = gpgme_op_verify_result (ctx);
813 for (sig = result->signatures, i = 0; sig && (i < idx);
814 sig = sig->next, i++);
816 state_attach_puts (_("Warning: The signature expired at: "), s);
817 print_time (sig ? sig->exp_timestamp : 0, s);
818 state_attach_puts ("\n", s);
821 if ((sum & GPGME_SIGSUM_KEY_MISSING))
822 state_attach_puts (_("Can't verify due to a missing "
823 "key or certificate\n"), s);
825 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
826 state_attach_puts (_("The CRL is not available\n"), s);
830 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
831 state_attach_puts (_("Available CRL is too old\n"), s);
835 if ((sum & GPGME_SIGSUM_BAD_POLICY))
836 state_attach_puts (_("A policy requirement was not met\n"), s);
838 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
839 const char *t0 = NULL, *t1 = NULL;
840 gpgme_verify_result_t result;
841 gpgme_signature_t sig;
844 state_attach_puts (_("A system error occurred"), s);
846 /* Try to figure out some more detailed system error information. */
847 result = gpgme_op_verify_result (ctx);
848 for (sig = result->signatures, i = 0; sig && (i < idx);
849 sig = sig->next, i++);
852 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
856 state_attach_puts (": ", s);
858 state_attach_puts (t0, s);
859 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
861 state_attach_puts (",", s);
862 state_attach_puts (t1, s);
865 state_attach_puts ("\n", s);
872 static void show_fingerprint (gpgme_key_t key, STATE * state)
877 const char *prefix = _("Fingerprint: ");
882 s = key->subkeys ? key->subkeys->fpr : NULL;
885 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
887 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
888 buf = p_new(char, bufsize);
889 m_strcpy(buf, bufsize, prefix);
890 p = buf + m_strlen(buf);
891 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
892 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
903 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
906 *p++ = is_pgp ? ' ' : ':';
907 if (is_pgp && i == 7)
912 /* just in case print remaining odd digits */
917 state_attach_puts (buf, state);
921 /* Show the valididy of a key used for one signature. */
922 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
924 gpgme_verify_result_t result = NULL;
925 gpgme_signature_t sig = NULL;
926 const char *txt = NULL;
928 result = gpgme_op_verify_result (ctx);
930 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
932 switch (sig ? sig->validity : 0) {
933 case GPGME_VALIDITY_UNKNOWN:
934 txt = _("WARNING: We have NO indication whether "
935 "the key belongs to the person named " "as shown above\n");
937 case GPGME_VALIDITY_UNDEFINED:
939 case GPGME_VALIDITY_NEVER:
940 txt = _("WARNING: The key does NOT BELONG to "
941 "the person named as shown above\n");
943 case GPGME_VALIDITY_MARGINAL:
944 txt = _("WARNING: It is NOT certain that the key "
945 "belongs to the person named as shown above\n");
947 case GPGME_VALIDITY_FULL:
948 case GPGME_VALIDITY_ULTIMATE:
953 state_attach_puts (txt, s);
956 /* Show information about one signature. This fucntion is called with
957 the context CTX of a sucessful verification operation and the
958 enumerator IDX which should start at 0 and incremete for each
961 Return values are: 0 for normal procession, 1 for a bad signature,
962 2 for a signature with a warning or -1 for no more signature. */
963 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
966 const char *fpr, *uid;
967 gpgme_key_t key = NULL;
968 int i, anybad = 0, anywarn = 0;
970 gpgme_user_id_t uids = NULL;
971 gpgme_verify_result_t result;
972 gpgme_signature_t sig;
973 gpgme_error_t err = GPG_ERR_NO_ERROR;
975 result = gpgme_op_verify_result (ctx);
977 /* FIXME: this code should use a static variable and remember
978 the current position in the list of signatures, IMHO.
981 for (i = 0, sig = result->signatures; sig && (i < idx);
982 i++, sig = sig->next);
984 return -1; /* Signature not found. */
987 gpgme_key_unref(signature_key);
988 signature_key = NULL;
991 created = sig->timestamp;
995 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
998 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1000 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1002 signature_key = key;
1005 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1006 error. Do it here to avoid a double free. */
1010 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1012 state_attach_puts (_("Error getting key information: "), s);
1013 state_attach_puts (gpg_strerror (err), s);
1014 state_attach_puts ("\n", s);
1017 else if ((sum & GPGME_SIGSUM_GREEN)) {
1018 state_attach_puts (_("Good signature from: "), s);
1019 state_attach_puts (uid, s);
1020 state_attach_puts ("\n", s);
1021 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1023 /* Skip primary UID. */
1027 state_attach_puts (_(" aka: "), s);
1028 state_attach_puts (uids->uid, s);
1029 state_attach_puts ("\n", s);
1031 state_attach_puts (_(" created: "), s);
1032 print_time (created, s);
1033 state_attach_puts ("\n", s);
1034 if (show_sig_summary (sum, ctx, key, idx, s))
1036 show_one_sig_validity (ctx, idx, s);
1038 else if ((sum & GPGME_SIGSUM_RED)) {
1039 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1040 state_attach_puts (uid, s);
1041 state_attach_puts ("\n", s);
1042 show_sig_summary (sum, ctx, key, idx, s);
1044 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1045 signature, so we display what a PGP user expects: The name,
1046 fingerprint and the key validity (which is neither fully or
1048 state_attach_puts (_("Good signature from: "), s);
1049 state_attach_puts (uid, s);
1050 state_attach_puts ("\n", s);
1051 state_attach_puts (_(" created: "), s);
1052 print_time (created, s);
1053 state_attach_puts ("\n", s);
1054 show_one_sig_validity (ctx, idx, s);
1055 show_fingerprint (key, s);
1056 if (show_sig_summary (sum, ctx, key, idx, s))
1059 else { /* can't decide (yellow) */
1061 state_attach_puts (_("Error checking signature"), s);
1062 state_attach_puts ("\n", s);
1063 show_sig_summary (sum, ctx, key, idx, s);
1066 if (key != signature_key)
1067 gpgme_key_unref(key);
1070 return anybad ? 1 : anywarn ? 2 : 0;
1073 /* Do the actual verification step. With IS_SMIME set to true we
1074 assume S/MIME (surprise!) */
1075 static int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
1081 gpgme_data_t signature, message;
1083 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1087 /* We need to tell gpgme about the encoding because the backend can't
1088 auto-detect plain base-64 encoding which is used by S/MIME. */
1090 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1092 err = gpgme_data_new_from_stream(&message, fp);
1094 gpgme_data_release (signature);
1095 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1098 ctx = create_gpgme_context (is_smime);
1100 /* Note: We don't need a current time output because GPGME avoids
1101 such an attack by separating the meta information from the
1103 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1105 err = gpgme_op_verify (ctx, signature, message, NULL);
1106 mutt_need_hard_redraw ();
1110 snprintf (buf, sizeof (buf) - 1,
1111 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1112 state_attach_puts (buf, s);
1114 else { /* Verification succeeded, see what the result is. */
1118 if (signature_key) {
1119 gpgme_key_unref(signature_key);
1120 signature_key = NULL;
1123 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1134 gpgme_verify_result_t result;
1135 gpgme_sig_notation_t notation;
1136 gpgme_signature_t sig;
1138 result = gpgme_op_verify_result (ctx);
1140 for (sig = result->signatures; sig; sig = sig->next) {
1141 if (sig->notations) {
1142 state_attach_puts ("*** Begin Notation (signature by: ", s);
1143 state_attach_puts (sig->fpr, s);
1144 state_attach_puts (") ***\n", s);
1145 for (notation = sig->notations; notation; notation = notation->next)
1147 if (notation->name) {
1148 state_attach_puts (notation->name, s);
1149 state_attach_puts ("=", s);
1151 if (notation->value) {
1152 state_attach_puts (notation->value, s);
1153 if (!(*notation->value
1154 && (notation->value[m_strlen(notation->value) - 1] ==
1156 state_attach_puts ("\n", s);
1159 state_attach_puts ("*** End Notation ***\n", s);
1165 gpgme_release (ctx);
1167 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1169 return badsig ? 1 : anywarn ? 2 : 0;
1172 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1173 IS_SMIME) with body A described further by state S. Write
1174 plaintext out to file FPOUT and return a new body. For PGP returns
1175 a flag in R_IS_SIGNED to indicate whether this is a combined
1176 encrypted and signed message, for S/MIME it returns true when it is
1177 not a encrypted but a signed message. */
1179 decrypt_part(BODY *a, STATE *s, FILE *fpout, int is_smime, int *r_is_signed)
1185 gpgme_data_t ciphertext, plaintext;
1186 int maybe_signed = 0;
1193 ctx = create_gpgme_context (is_smime);
1196 /* Make a data object from the body, create context etc. */
1197 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1200 plaintext = create_gpgme_data ();
1202 /* Do the decryption or the verification in case of the S/MIME hack. */
1203 if ((!is_smime) || maybe_signed) {
1205 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1206 else if (maybe_signed)
1207 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1210 /* Check wether signatures have been verified. */
1211 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1213 if (verify_result->signatures)
1218 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1219 gpgme_data_release (ciphertext);
1221 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1222 /* Check whether this might be a signed message despite what
1223 the mime header told us. Retry then. gpgsm returns the
1224 error information "unsupported Algorithm '?'" but gpgme
1225 will not store this unknown algorithm, thus we test that
1226 it has not been set. */
1227 gpgme_decrypt_result_t result;
1229 result = gpgme_op_decrypt_result (ctx);
1230 if (!result->unsupported_algorithm) {
1232 gpgme_data_release (plaintext);
1236 mutt_need_hard_redraw ();
1237 if ((s->flags & M_DISPLAY)) {
1240 snprintf (buf, sizeof (buf) - 1,
1241 _("[-- Error: decryption failed: %s --]\n\n"),
1242 gpgme_strerror (err));
1243 state_attach_puts (buf, s);
1245 gpgme_data_release (plaintext);
1246 gpgme_release (ctx);
1249 mutt_need_hard_redraw ();
1251 /* Read the output from GPGME, and make sure to change CRLF to LF,
1252 otherwise read_mime_header has a hard time parsing the message. */
1253 if (data_object_to_stream (plaintext, fpout)) {
1254 gpgme_data_release (plaintext);
1255 gpgme_release (ctx);
1258 gpgme_data_release (plaintext);
1260 a->is_signed_data = 0;
1266 a->is_signed_data = 1;
1268 *r_is_signed = -1; /* A signature exists. */
1270 if ((s->flags & M_DISPLAY))
1271 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1272 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1278 if (!anybad && idx && r_is_signed && *r_is_signed)
1279 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1281 if ((s->flags & M_DISPLAY))
1282 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1284 gpgme_release (ctx);
1289 tattach = mutt_read_mime_header (fpout, 0);
1292 * Need to set the length of this body part.
1294 fstat (fileno (fpout), &info);
1295 tattach->length = info.st_size - tattach->offset;
1297 tattach->warnsig = anywarn;
1299 /* See if we need to recurse on this MIME part. */
1300 mutt_parse_part (fpout, tattach);
1306 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1307 the stream in CUR and FPOUT. Returns 0 on success. */
1308 int crypt_pgp_decrypt_mime (FILE * fpin, FILE **fpout, BODY *b, BODY **cur)
1311 BODY *first_part = b;
1314 first_part->goodsig = 0;
1315 first_part->warnsig = 0;
1317 if (!mutt_is_multipart_encrypted(b) || !b->parts || !b->parts->next)
1326 mutt_perror (_("Can't create temporary file"));
1330 *cur = decrypt_part(b, &s, *fpout, 0, &is_signed);
1332 first_part->goodsig = is_signed > 0;
1333 return *cur ? 0 : -1;
1337 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1338 the stream in CUR and FPOUT. Returns 0 on success. */
1339 int crypt_smime_decrypt_mime(FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
1344 long saved_b_offset;
1345 ssize_t saved_b_length;
1348 if (!mutt_is_application_smime (b))
1354 /* Decode the body - we need to pass binary CMS to the
1355 backend. The backend allows for Base64 encoded data but it does
1356 not allow for QP which I have seen in some messages. So better
1358 saved_b_type = b->type;
1359 saved_b_offset = b->offset;
1360 saved_b_length = b->length;
1363 fseeko (s.fpin, b->offset, 0);
1366 mutt_perror (_("Can't create temporary file"));
1371 mutt_decode_attachment (b, &s);
1373 b->length = ftello (s.fpout);
1382 mutt_perror (_("Can't create temporary file"));
1386 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1388 (*cur)->goodsig = is_signed > 0;
1389 b->type = saved_b_type;
1390 b->length = saved_b_length;
1391 b->offset = saved_b_offset;
1394 if (*cur && !is_signed && !(*cur)->parts
1395 && mutt_is_application_smime (*cur)) {
1396 /* Assume that this is a opaque signed s/mime message. This is
1397 an ugly way of doing it but we have anyway a problem with
1398 arbitrary encoded S/MIME messages: Only the outer part may be
1399 encrypted. The entire mime parsing should be revamped,
1400 probably by keeping the temportary files so that we don't
1401 need to decrypt them all the time. Inner parts of an
1402 encrypted part can then pint into this file and tehre won't
1403 never be a need to decrypt again. This needs a partial
1404 rewrite of the MIME engine. */
1408 saved_b_type = bb->type;
1409 saved_b_offset = bb->offset;
1410 saved_b_length = bb->length;
1413 fseeko (s.fpin, bb->offset, 0);
1416 mutt_perror (_("Can't create temporary file"));
1421 mutt_decode_attachment (bb, &s);
1423 bb->length = ftello (s.fpout);
1433 mutt_perror (_("Can't create temporary file"));
1437 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1439 tmp_b->goodsig = is_signed > 0;
1440 bb->type = saved_b_type;
1441 bb->length = saved_b_length;
1442 bb->offset = saved_b_offset;
1445 body_list_wipe(cur);
1448 return *cur ? 0 : -1;
1453 pgp_check_traditional_one_body(FILE *fp, BODY *b, int tagged_only)
1455 char tempfile[_POSIX_PATH_MAX];
1456 char buf[HUGE_STRING];
1463 if (b->type != TYPETEXT)
1466 if (tagged_only && !b->tagged)
1469 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1470 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1475 if ((tfp = fopen(tempfile, "r")) == NULL) {
1480 while (fgets (buf, sizeof (buf), tfp)) {
1481 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1482 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1484 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1494 /* fix the content type */
1496 parameter_setval(&b->parameter, "format", "fixed");
1497 parameter_setval(&b->parameter, "x-action",
1498 enc ? "pgp-encrypted" : "pgp-signed");
1502 int crypt_pgp_check_traditional(FILE *fp, BODY *b, int tagged_only)
1506 for (; b; b = b->next) {
1507 if (is_multipart(b))
1508 rv |= crypt_pgp_check_traditional(fp, b->parts, tagged_only);
1509 if (b->type == TYPETEXT) {
1511 if ((r = mutt_is_application_pgp(b))) {
1514 rv |= pgp_check_traditional_one_body(fp, b, tagged_only);
1523 Copy a clearsigned message, and strip the signature and PGP's
1526 XXX - charset handling: We assume that it is safe to do
1527 character set decoding first, dash decoding second here, while
1528 we do it the other way around in the main handler.
1530 (Note that we aren't worse than Outlook & Cie in this, and also
1531 note that we can successfully handle anything produced by any
1532 existing versions of mutt.) */
1533 static void copy_clearsigned(gpgme_data_t data, STATE * s, char *charset)
1535 char buf[HUGE_STRING];
1536 short complete, armor_header;
1541 fname = data_object_to_tempfile(data, &fp);
1547 fc = fgetconv_open (fp, charset, MCharset.charset, M_ICONV_HOOK_FROM);
1549 for (complete = 1, armor_header = 1;
1550 fgetconvs (buf, sizeof (buf), fc) != NULL;
1551 complete = strchr (buf, '\n') != NULL) {
1554 state_puts (buf, s);
1558 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1568 state_puts (s->prefix, s);
1570 if (buf[0] == '-' && buf[1] == ' ')
1571 state_puts (buf + 2, s);
1573 state_puts (buf, s);
1576 fgetconv_close (&fc);
1580 /* Support for classic_application/pgp */
1581 int crypt_pgp_application_pgp_handler(BODY *m, STATE *s)
1583 int needpass = -1, pgp_keyblock = 0;
1587 off_t last_pos, offset;
1588 char buf[HUGE_STRING];
1589 FILE *pgpout = NULL;
1591 gpgme_error_t err = 0;
1592 gpgme_data_t armored_data = NULL;
1594 short maybe_goodsig = 1;
1595 short have_any_sigs = 0;
1597 char body_charset[STRING]; /* Only used for clearsigned messages. */
1599 /* For clearsigned messages we won't be able to get a character set
1600 but we know that this may only be text thus we assume Latin-1
1602 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1603 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1605 fseeko (s->fpin, m->offset, 0);
1606 last_pos = m->offset;
1608 for (bytes = m->length; bytes > 0;) {
1609 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1612 offset = ftello (s->fpin);
1613 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1616 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1618 start_pos = last_pos;
1620 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1622 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1626 else if (!m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1631 /* XXX - we may wish to recode here */
1633 state_puts (s->prefix, s);
1634 state_puts (buf, s);
1638 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1640 /* Copy PGP material to an data container */
1641 armored_data = create_gpgme_data ();
1642 gpgme_data_write (armored_data, buf, m_strlen(buf));
1643 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1644 offset = ftello (s->fpin);
1645 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1648 gpgme_data_write (armored_data, buf, m_strlen(buf));
1650 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1652 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1653 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1658 /* Invoke PGP if needed */
1659 if (!clearsign || (s->flags & M_VERIFY)) {
1660 unsigned int sig_stat = 0;
1661 gpgme_data_t plaintext;
1664 plaintext = create_gpgme_data ();
1665 ctx = create_gpgme_context (0);
1668 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1670 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1671 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1672 /* Decrypt verify can't handle signed only messages. */
1673 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1674 ? gpgme_error_from_errno (errno) : 0;
1675 /* Must release plaintext so that we supply an
1676 uninitialized object. */
1677 gpgme_data_release (plaintext);
1678 plaintext = create_gpgme_data ();
1679 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1686 snprintf (errbuf, sizeof (errbuf) - 1,
1687 _("Error: decryption/verification failed: %s\n"),
1688 gpgme_strerror (err));
1689 state_attach_puts (errbuf, s);
1691 else { /* Decryption/Verification succeeded */
1695 /* Check wether signatures have been verified. */
1696 gpgme_verify_result_t verify_result;
1698 verify_result = gpgme_op_verify_result (ctx);
1699 if (verify_result->signatures)
1705 if ((s->flags & M_DISPLAY) && sig_stat) {
1710 state_attach_puts (_("[-- Begin signature "
1711 "information --]\n"), s);
1714 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1723 state_attach_puts (_("[-- End signature "
1724 "information --]\n\n"), s);
1727 tmpfname = data_object_to_tempfile(plaintext, &pgpout);
1730 state_attach_puts (_("Error: copy data failed\n"), s);
1734 p_delete(&tmpfname);
1737 gpgme_release (ctx);
1741 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1742 * outputs utf-8 cleartext. This may not always be true, but it
1743 * seems to be a reasonable guess.
1746 if (s->flags & M_DISPLAY) {
1748 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1749 else if (pgp_keyblock)
1750 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1752 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1756 copy_clearsigned (armored_data, s, body_charset);
1763 fc = fgetconv_open (pgpout, "utf-8", MCharset.charset, 0);
1764 while ((c = fgetconv (fc)) != EOF) {
1766 if (c == '\n' && s->prefix)
1767 state_puts (s->prefix, s);
1769 fgetconv_close (&fc);
1772 if (s->flags & M_DISPLAY) {
1773 state_putc ('\n', s);
1775 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1776 else if (pgp_keyblock)
1777 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1779 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1787 /* XXX - we may wish to recode here */
1789 state_puts (s->prefix, s);
1790 state_puts (buf, s);
1794 m->goodsig = (maybe_goodsig && have_any_sigs);
1796 if (needpass == -1) {
1797 state_attach_puts (_("[-- Error: could not find beginning"
1798 " of PGP message! --]\n\n"), s);
1804 /* MIME handler for pgp/mime encrypted messages. */
1805 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1807 char tempfile[_POSIX_PATH_MAX];
1810 BODY *orig_body = a;
1815 if (!a || a->type != TYPEAPPLICATION || !a->subtype
1816 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
1817 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
1818 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
1819 if (s->flags & M_DISPLAY)
1820 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
1825 /* Move forward to the application/pgp-encrypted body. */
1828 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1830 if (s->flags & M_DISPLAY)
1831 state_attach_puts (_("[-- Error: could not create temporary file! "
1836 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
1838 tattach->goodsig = is_signed > 0;
1840 if (s->flags & M_DISPLAY)
1841 state_attach_puts (is_signed ?
1843 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
1844 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
1847 FILE *savefp = s->fpin;
1850 rc = mutt_body_handler (tattach, s);
1855 * if a multipart/signed is the _only_ sub-part of a
1856 * multipart/encrypted, cache signature verification
1859 if (mutt_is_multipart_signed (tattach) && !tattach->next)
1860 orig_body->goodsig |= tattach->goodsig;
1862 if (s->flags & M_DISPLAY) {
1863 state_puts ("\n", s);
1864 state_attach_puts (is_signed ?
1866 ("[-- End of PGP/MIME signed and encrypted data --]\n")
1867 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
1870 body_list_wipe(&tattach);
1874 mutt_unlink (tempfile);
1878 /* Support for application/smime */
1879 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
1881 char tempfile[_POSIX_PATH_MAX];
1888 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1890 if (s->flags & M_DISPLAY)
1891 state_attach_puts (_("[-- Error: could not create temporary file! "
1896 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
1898 tattach->goodsig = is_signed > 0;
1900 if (s->flags & M_DISPLAY)
1901 state_attach_puts (is_signed ?
1902 _("[-- The following data is S/MIME signed --]\n\n") :
1903 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
1906 FILE *savefp = s->fpin;
1909 rc = mutt_body_handler (tattach, s);
1914 * if a multipart/signed is the _only_ sub-part of a
1915 * multipart/encrypted, cache signature verification
1918 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
1919 if (!(a->goodsig = tattach->goodsig))
1920 a->warnsig = tattach->warnsig;
1922 else if (tattach->goodsig) {
1924 a->warnsig = tattach->warnsig;
1927 if (s->flags & M_DISPLAY) {
1928 state_puts ("\n", s);
1929 state_attach_puts (is_signed ?
1930 _("[-- End of S/MIME signed data --]\n") :
1931 _("[-- End of S/MIME encrypted data --]\n"), s);
1934 body_list_wipe(&tattach);
1938 mutt_unlink (tempfile);
1944 * Format an entry on the CRYPT key selection menu.
1947 * %k key id %K key id of the principal key
1949 * %a algorithm %A algorithm of the princ. key
1950 * %l length %L length of the princ. key
1951 * %f flags %F flags of the princ. key
1952 * %c capabilities %C capabilities of the princ. key
1953 * %t trust/validity of the key-uid association
1955 * %[...] date of key using strftime(3)
1959 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
1960 const char *src, const char *prefix,
1961 const char *ifstr, const char *elstr,
1962 anytype data, format_flag flags)
1965 crypt_entry_t *entry;
1968 int optional = (flags & M_FORMAT_OPTIONAL);
1969 const char *s = NULL;
1975 /* if (isupper ((unsigned char) op)) */
1978 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
1981 switch (ascii_tolower (op)) {
1985 char buf2[STRING], *p;
2001 while (len > 0 && *cp != ']') {
2010 break; /* not enough space */
2020 if (do_locales && Locale)
2021 setlocale (LC_TIME, Locale);
2026 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2027 tt = key->kobj->subkeys->timestamp;
2029 tm = localtime (&tt);
2031 strftime (buf2, sizeof (buf2), dest, tm);
2034 setlocale (LC_TIME, "C");
2036 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2037 snprintf (dest, destlen, fmt, buf2);
2044 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2045 snprintf (dest, destlen, fmt, entry->num);
2050 /* fixme: we need a way to distinguish between main and subkeys.
2051 Store the idx in entry? */
2052 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2053 snprintf (dest, destlen, fmt, crypt_keyid (key));
2058 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2059 snprintf (dest, destlen, fmt, key->uid);
2064 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2065 if (key->kobj->subkeys)
2066 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2069 snprintf (dest, destlen, fmt, s);
2074 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2075 if (key->kobj->subkeys)
2076 val = key->kobj->subkeys->length;
2079 snprintf (dest, destlen, fmt, val);
2084 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2085 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2087 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2092 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2093 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2095 else if (!(kflags & (KEYFLAG_ABILITIES)))
2099 if ((kflags & KEYFLAG_ISX509))
2102 gpgme_user_id_t uid = NULL;
2105 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2106 i++, uid = uid->next);
2108 switch (uid->validity) {
2109 case GPGME_VALIDITY_UNDEFINED:
2112 case GPGME_VALIDITY_NEVER:
2115 case GPGME_VALIDITY_MARGINAL:
2118 case GPGME_VALIDITY_FULL:
2121 case GPGME_VALIDITY_ULTIMATE:
2124 case GPGME_VALIDITY_UNKNOWN:
2130 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2131 snprintf (dest, destlen, fmt, s ? *s : 'B');
2134 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2135 snprintf (dest, destlen, fmt,
2136 gpgme_get_protocol_name (key->kobj->protocol));
2143 if (flags & M_FORMAT_OPTIONAL)
2144 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2145 mutt_attach_fmt, data, 0);
2149 /* Used by the display fucntion to format a line. */
2150 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2152 cryptkey_t **cryptkey_table = (cryptkey_t **) menu->data;
2153 crypt_entry_t entry;
2155 entry.key = cryptkey_table[num];
2156 entry.num = num + 1;
2158 m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
2159 option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
2162 /* Compare two addresses and the keyid to be used for sorting. */
2163 static int _crypt_compare_address (const void *a, const void *b)
2165 cryptkey_t **s = (cryptkey_t **) a;
2166 cryptkey_t **t = (cryptkey_t **) b;
2169 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2172 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2175 static int crypt_compare_address (const void *a, const void *b)
2177 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2178 : _crypt_compare_address (a, b));
2182 /* Compare two key IDs and the addresses to be used for sorting. */
2183 static int _crypt_compare_keyid (const void *a, const void *b)
2185 cryptkey_t **s = (cryptkey_t **) a;
2186 cryptkey_t **t = (cryptkey_t **) b;
2189 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2192 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2195 static int crypt_compare_keyid (const void *a, const void *b)
2197 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2198 : _crypt_compare_keyid (a, b));
2201 /* Compare 2 creation dates and the addresses. For sorting. */
2202 static int _crypt_compare_date (const void *a, const void *b)
2204 cryptkey_t **s = (cryptkey_t **) a;
2205 cryptkey_t **t = (cryptkey_t **) b;
2206 unsigned long ts = 0, tt = 0;
2208 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2209 ts = (*s)->kobj->subkeys->timestamp;
2210 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2211 tt = (*t)->kobj->subkeys->timestamp;
2218 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2221 static int crypt_compare_date (const void *a, const void *b)
2223 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2224 : _crypt_compare_date (a, b));
2227 /* Compare two trust values, the key length, the creation dates. the
2228 addresses and the key IDs. For sorting. */
2229 static int _crypt_compare_trust (const void *a, const void *b)
2231 cryptkey_t **s = (cryptkey_t **) a;
2232 cryptkey_t **t = (cryptkey_t **) b;
2233 unsigned long ts = 0, tt = 0;
2236 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2237 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2240 if ((*s)->kobj->uids)
2241 ts = (*s)->kobj->uids->validity;
2242 if ((*t)->kobj->uids)
2243 tt = (*t)->kobj->uids->validity;
2244 if ((r = (tt - ts)))
2247 if ((*s)->kobj->subkeys)
2248 ts = (*s)->kobj->subkeys->length;
2249 if ((*t)->kobj->subkeys)
2250 tt = (*t)->kobj->subkeys->length;
2254 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2255 ts = (*s)->kobj->subkeys->timestamp;
2256 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2257 tt = (*t)->kobj->subkeys->timestamp;
2263 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2265 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2268 static int crypt_compare_trust (const void *a, const void *b)
2270 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2271 : _crypt_compare_trust (a, b));
2274 /* Print the X.500 Distinguished Name part KEY from the array of parts
2276 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2280 for (; dn->key; dn++) {
2281 if (!m_strcmp(dn->key, key)) {
2284 print_utf8 (fp, dn->value, m_strlen(dn->value));
2291 /* Print all parts of a DN in a standard sequence. */
2292 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2294 const char *stdpart[] = {
2295 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2297 int any = 0, any2 = 0, i;
2299 for (i = 0; stdpart[i]; i++) {
2302 any = print_dn_part (fp, dn, stdpart[i]);
2304 /* now print the rest without any specific ordering */
2305 for (; dn->key; dn++) {
2306 for (i = 0; stdpart[i]; i++) {
2307 if (!m_strcmp(dn->key, stdpart[i]))
2315 any = print_dn_part (fp, dn, dn->key);
2324 /* Parse an RDN; this is a helper to parse_dn(). */
2325 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2326 const unsigned char *string)
2328 const unsigned char *s, *s1;
2332 /* parse attributeType */
2333 for (s = string + 1; *s && *s != '='; s++);
2335 return NULL; /* error */
2338 return NULL; /* empty key */
2339 array->key = p_dupstr(string, n );
2340 p = (unsigned char *) array->key;
2343 if (*string == '#') { /* hexstring */
2345 for (s = string; hexval(*s) >= 0; s++)
2349 return NULL; /* empty or odd number of digits */
2351 p = p_new(unsigned char, n + 1);
2352 array->value = (char *) p;
2353 for (s1 = string; n; s1 += 2, n--)
2354 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2357 else { /* regular v3 quoted string */
2358 for (n = 0, s = string; *s; s++) {
2359 if (*s == '\\') { /* pair */
2361 if (*s == ',' || *s == '=' || *s == '+'
2362 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2363 || *s == '\\' || *s == '\"' || *s == ' ')
2365 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2370 return NULL; /* invalid escape sequence */
2372 else if (*s == '\"')
2373 return NULL; /* invalid encoding */
2374 else if (*s == ',' || *s == '=' || *s == '+'
2375 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2381 p = p_new(unsigned char, n + 1);
2382 array->value = (char *) p;
2383 for (s = string; n; s++, n--) {
2386 if (hexval(*s) >= 0) {
2387 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2402 /* Parse a DN and return an array-ized one. This is not a validating
2403 parser and it does not support any old-stylish syntax; gpgme is
2404 expected to return only rfc2253 compatible strings. */
2405 static struct dn_array_s *parse_dn (const unsigned char *string)
2407 struct dn_array_s *array;
2408 ssize_t arrayidx, arraysize;
2411 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2412 array = p_new(struct dn_array_s, arraysize + 1);
2415 while (*string == ' ')
2419 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2420 struct dn_array_s *a2;
2423 a2 = p_new(struct dn_array_s, arraysize + 1);
2424 for (i = 0; i < arrayidx; i++) {
2425 a2[i].key = array[i].key;
2426 a2[i].value = array[i].value;
2431 array[arrayidx].key = NULL;
2432 array[arrayidx].value = NULL;
2433 string = parse_dn_part (array + arrayidx, string);
2437 while (*string == ' ')
2439 if (*string && *string != ',' && *string != ';' && *string != '+')
2440 goto failure; /* invalid delimiter */
2444 array[arrayidx].key = NULL;
2445 array[arrayidx].value = NULL;
2449 for (i = 0; i < arrayidx; i++) {
2450 p_delete(&array[i].key);
2451 p_delete(&array[i].value);
2458 /* Print a nice representation of the USERID and make sure it is
2459 displayed in a proper way, which does mean to reorder some parts
2460 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2461 functions. It is utf-8 encoded. */
2462 static void parse_and_print_user_id(FILE * fp, const char *userid)
2467 if (*userid == '<') {
2468 s = strchr (userid + 1, '>');
2470 print_utf8 (fp, userid + 1, s - userid - 1);
2472 else if (*userid == '(')
2473 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2474 else if (*userid & ~127 || __m_strdigits[(int)*userid] == 255)
2475 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2477 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2480 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2482 print_dn_parts (fp, dn);
2483 for (i = 0; dn[i].key; i++) {
2484 p_delete(&dn[i].key);
2485 p_delete(&dn[i].value);
2493 KEY_CAP_CAN_ENCRYPT,
2498 static unsigned int key_check_cap(gpgme_key_t key, key_cap_t cap)
2500 gpgme_subkey_t subkey = NULL;
2501 unsigned int ret = 0;
2504 case KEY_CAP_CAN_ENCRYPT:
2505 if (!(ret = key->can_encrypt))
2506 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2507 if ((ret = subkey->can_encrypt))
2510 case KEY_CAP_CAN_SIGN:
2511 if (!(ret = key->can_sign))
2512 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2513 if ((ret = subkey->can_sign))
2516 case KEY_CAP_CAN_CERTIFY:
2517 if (!(ret = key->can_certify))
2518 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2519 if ((ret = subkey->can_certify))
2528 /* Print verbose information about a key or certificate to FP. */
2529 static void print_key_info (gpgme_key_t key, FILE * fp)
2532 const char *s = NULL, *s2 = NULL;
2535 char shortbuf[STRING];
2536 unsigned long aval = 0;
2540 gpgme_user_id_t uid = NULL;
2543 setlocale (LC_TIME, Locale);
2545 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2547 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2552 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2555 fputs (_("[Invalid]"), fp);
2559 print_utf8 (fp, s, m_strlen(s));
2561 parse_and_print_user_id (fp, s);
2565 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2566 tt = key->subkeys->timestamp;
2568 tm = localtime (&tt);
2569 #ifdef HAVE_LANGINFO_D_T_FMT
2570 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2572 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2574 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2577 if (key->subkeys && (key->subkeys->expires > 0)) {
2578 tt = key->subkeys->expires;
2580 tm = localtime (&tt);
2581 #ifdef HAVE_LANGINFO_D_T_FMT
2582 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2584 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2586 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2590 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2594 s2 = is_pgp ? "PGP" : "X.509";
2597 aval = key->subkeys->length;
2599 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2601 fprintf (fp, _("Key Usage .: "));
2604 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2605 fprintf (fp, "%s%s", delim, _("encryption"));
2608 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2609 fprintf (fp, "%s%s", delim, _("signing"));
2612 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2613 fprintf (fp, "%s%s", delim, _("certification"));
2619 s = key->subkeys->fpr;
2620 fputs (_("Fingerprint: "), fp);
2621 if (is_pgp && m_strlen(s) == 40) {
2622 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2627 putc (is_pgp ? ' ' : ':', fp);
2628 if (is_pgp && i == 4)
2633 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2636 putc (is_pgp ? ' ' : ':', fp);
2637 if (is_pgp && i == 7)
2641 fprintf (fp, "%s\n", s);
2644 if (key->issuer_serial) {
2645 s = key->issuer_serial;
2647 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2650 if (key->issuer_name) {
2651 s = key->issuer_name;
2653 fprintf (fp, _("Issued By .: "));
2654 parse_and_print_user_id (fp, s);
2659 /* For PGP we list all subkeys. */
2661 gpgme_subkey_t subkey = NULL;
2663 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2667 if (m_strlen(s) == 16)
2668 s += 8; /* display only the short keyID */
2669 fprintf (fp, _("Subkey ....: 0x%s"), s);
2670 if (subkey->revoked) {
2672 fputs (_("[Revoked]"), fp);
2674 if (subkey->invalid) {
2676 fputs (_("[Invalid]"), fp);
2678 if (subkey->expired) {
2680 fputs (_("[Expired]"), fp);
2682 if (subkey->disabled) {
2684 fputs (_("[Disabled]"), fp);
2688 if (subkey->timestamp > 0) {
2689 tt = subkey->timestamp;
2691 tm = localtime (&tt);
2692 #ifdef HAVE_LANGINFO_D_T_FMT
2693 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2695 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2697 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2700 if (subkey->expires > 0) {
2701 tt = subkey->expires;
2703 tm = localtime (&tt);
2704 #ifdef HAVE_LANGINFO_D_T_FMT
2705 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2707 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2709 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2713 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2718 aval = subkey->length;
2722 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2724 fprintf (fp, _("Key Usage .: "));
2727 if (subkey->can_encrypt) {
2728 fprintf (fp, "%s%s", delim, _("encryption"));
2731 if (subkey->can_sign) {
2732 fprintf (fp, "%s%s", delim, _("signing"));
2735 if (subkey->can_certify) {
2736 fprintf (fp, "%s%s", delim, _("certification"));
2744 setlocale (LC_TIME, "C");
2748 /* Show detailed information about the selected key */
2749 static void verify_key (cryptkey_t * key)
2752 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2754 gpgme_ctx_t listctx = NULL;
2756 gpgme_key_t k = NULL;
2759 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2761 mutt_perror (_("Can't create temporary file"));
2764 mutt_message _("Collecting data...");
2766 print_key_info (key->kobj, fp);
2768 err = gpgme_new (&listctx);
2770 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2771 gpgme_strerror (err));
2774 if ((key->flags & KEYFLAG_ISX509))
2775 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2779 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2781 err = gpgme_op_keylist_start (listctx, s, 0);
2785 err = gpgme_op_keylist_next (listctx, &k);
2787 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2790 gpgme_op_keylist_end (listctx);
2792 print_key_info (k, fp);
2795 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2802 gpgme_release (listctx);
2804 mutt_clear_error ();
2805 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2806 mutt_pager(cmd, tempfile, 0, NULL);
2809 /* Implementation of `findkeys'. */
2811 static void add_hints(string_array *arr, const char *s)
2817 int l = strcspn(s, " ,.:\"()<>\n");
2818 string_array_append(arr, p_dupstr(s, l));
2820 s += strspn(s, " ,.:\"()<>\n");
2824 /* Return a list of keys which are candidates for the selection. */
2826 get_candidates(string_array *hints, unsigned int app, int secret)
2828 cryptkey_t *res = NULL, **kend = &res;
2833 if (hints->len <= 0)
2835 string_array_append(hints, NULL);
2836 ctx = create_gpgme_context(0);
2838 if ((app & APPLICATION_PGP)) {
2839 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2842 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2843 gpgme_strerror(err));
2848 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2849 gpgme_user_id_t uid = NULL;
2850 unsigned int flags = 0;
2853 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2854 flags |= KEYFLAG_CANENCRYPT;
2855 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2856 flags |= KEYFLAG_CANSIGN;
2858 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2859 cryptkey_t *k = p_new(cryptkey_t, 1);
2868 if (gpg_err_code(err) != GPG_ERR_EOF)
2869 mutt_error(_("gpgme_op_keylist_next failed: %s"), gpgme_strerror(err));
2870 gpgme_op_keylist_end(ctx);
2873 if ((app & APPLICATION_SMIME)) {
2874 gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
2875 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2878 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2879 gpgme_strerror(err));
2884 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2885 gpgme_user_id_t uid = NULL;
2886 unsigned int flags = KEYFLAG_ISX509;
2889 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2890 flags |= KEYFLAG_CANENCRYPT;
2891 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2892 flags |= KEYFLAG_CANSIGN;
2894 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2895 cryptkey_t *k = p_new(cryptkey_t, 1);
2904 if (gpg_err_code(err) != GPG_ERR_EOF)
2905 mutt_error(_("gpgme_op_keylist_next failed: %s"),
2906 gpgme_strerror(err));
2907 gpgme_op_keylist_end(ctx);
2914 /* Display a menu to select a key from the array KEYS. FORCED_VALID
2915 will be set to true on return if the user did override the the
2917 static cryptkey_t *crypt_select_key (cryptkey_t * keys,
2918 address_t * p, const char *s,
2919 unsigned int app, int *forced_valid)
2922 cryptkey_t **cryptkey_table;
2925 char helpstr[STRING], buf[LONG_STRING];
2927 int (*f) (const void *, const void *);
2928 int menu_to_use = 0;
2933 /* build the key table */
2935 cryptkey_table = NULL;
2936 for (k = keys; k; k = k->next) {
2937 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
2944 p_realloc(&cryptkey_table, keymax);
2947 cryptkey_table[i++] = k;
2950 if (!i && unusable) {
2951 mutt_error _("All matching keys are marked expired/revoked.");
2957 switch (PgpSortKeys & SORT_MASK) {
2959 f = crypt_compare_date;
2962 f = crypt_compare_keyid;
2965 f = crypt_compare_address;
2969 f = crypt_compare_trust;
2972 qsort (cryptkey_table, i, sizeof (cryptkey_t *), f);
2974 if (app & APPLICATION_PGP)
2975 menu_to_use = MENU_KEY_SELECT_PGP;
2976 else if (app & APPLICATION_SMIME)
2977 menu_to_use = MENU_KEY_SELECT_SMIME;
2980 mutt_make_help (buf, sizeof (buf), _("Exit "), menu_to_use, OP_EXIT);
2981 m_strcat(helpstr, sizeof(helpstr), buf);
2982 mutt_make_help (buf, sizeof (buf), _("Select "), menu_to_use,
2983 OP_GENERIC_SELECT_ENTRY);
2984 m_strcat(helpstr, sizeof(helpstr), buf);
2985 mutt_make_help (buf, sizeof (buf), _("Check key "),
2986 menu_to_use, OP_VERIFY_KEY);
2987 m_strcat(helpstr, sizeof(helpstr), buf);
2988 mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
2989 m_strcat(helpstr, sizeof(helpstr), buf);
2991 menu = mutt_new_menu ();
2993 menu->make_entry = crypt_entry;
2994 menu->menu = menu_to_use;
2995 menu->help = helpstr;
2996 menu->data = cryptkey_table;
3001 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3002 ts = _("PGP and S/MIME keys matching");
3003 else if ((app & APPLICATION_PGP))
3004 ts = _("PGP keys matching");
3005 else if ((app & APPLICATION_SMIME))
3006 ts = _("S/MIME keys matching");
3008 ts = _("keys matching");
3011 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3013 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3017 mutt_clear_error ();
3021 switch (mutt_menuLoop (menu)) {
3023 verify_key (cryptkey_table[menu->current]);
3024 menu->redraw = REDRAW_FULL;
3028 mutt_message ("%s", cryptkey_table[menu->current]->uid);
3031 case OP_GENERIC_SELECT_ENTRY:
3032 /* FIXME make error reporting more verbose - this should be
3033 easy because gpgme provides more information */
3034 if (option (OPTPGPCHECKTRUST)) {
3035 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE ) {
3036 mutt_error(_("This key can't be used: "
3037 "expired/disabled/revoked."));
3042 if (option (OPTPGPCHECKTRUST) &&
3043 ((cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3044 || !crypt_id_is_strong (cryptkey_table[menu->current]))) {
3046 char buff[LONG_STRING];
3048 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3049 s = N_("ID is expired/disabled/revoked.");
3051 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3052 gpgme_user_id_t uid = NULL;
3057 uid = cryptkey_table[menu->current]->kobj->uids;
3058 for (j = 0; (j < cryptkey_table[menu->current]->idx) && uid;
3059 j++, uid = uid->next);
3061 val = uid->validity;
3064 case GPGME_VALIDITY_UNKNOWN:
3065 case GPGME_VALIDITY_UNDEFINED:
3066 warn_s = N_("ID has undefined validity.");
3068 case GPGME_VALIDITY_NEVER:
3069 warn_s = N_("ID is not valid.");
3071 case GPGME_VALIDITY_MARGINAL:
3072 warn_s = N_("ID is only marginally valid.");
3074 case GPGME_VALIDITY_FULL:
3075 case GPGME_VALIDITY_ULTIMATE:
3079 snprintf (buff, sizeof (buff),
3080 _("%s Do you really want to use the key?"), _(warn_s));
3082 if (mutt_yesorno (buff, 0) != 1) {
3083 mutt_clear_error ();
3090 k = cryptkey_dup(cryptkey_table[menu->current]);
3101 mutt_menuDestroy (&menu);
3102 p_delete(&cryptkey_table);
3104 set_option (OPTNEEDREDRAW);
3110 crypt_getkeybyaddr(address_t * a, int abilities, int app, int *forced_valid)
3117 int this_key_has_strong;
3118 int this_key_has_weak;
3119 int this_key_has_invalid;
3122 cryptkey_t *keys, *k;
3123 cryptkey_t *the_valid_key = NULL;
3124 cryptkey_t *matches = NULL;
3125 cryptkey_t **matches_endp = &matches;
3131 string_array_init(&hints);
3132 add_hints(&hints, a->mailbox);
3133 add_hints(&hints, a->personal);
3135 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3136 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3137 string_array_wipe(&hints);
3143 for (k = keys; k; k = k->next) {
3144 if (abilities && !(k->flags & abilities)) {
3148 this_key_has_weak = 0; /* weak but valid match */
3149 this_key_has_invalid = 0; /* invalid match */
3150 this_key_has_strong = 0; /* strong and valid match */
3151 match = 0; /* any match */
3153 r = rfc822_parse_adrlist (NULL, k->uid);
3154 for (p = r; p; p = p->next) {
3155 int validity = crypt_id_matches_addr (a, p, k);
3157 if (validity & CRYPT_KV_MATCH) /* something matches */
3160 /* is this key a strong candidate? */
3161 if ((validity & CRYPT_KV_VALID)
3162 && (validity & CRYPT_KV_STRONGID)
3163 && (validity & CRYPT_KV_ADDR)) {
3164 if (the_valid_key && the_valid_key != k)
3167 this_key_has_strong = 1;
3169 else if ((validity & CRYPT_KV_MATCH)
3170 && !(validity & CRYPT_KV_VALID))
3171 this_key_has_invalid = 1;
3172 else if ((validity & CRYPT_KV_MATCH)
3173 && (!(validity & CRYPT_KV_STRONGID)
3174 || !(validity & CRYPT_KV_ADDR)))
3175 this_key_has_weak = 1;
3177 address_list_wipe(&r);
3182 if (!this_key_has_strong && this_key_has_invalid)
3184 if (!this_key_has_strong && this_key_has_weak)
3187 *matches_endp = tmp = cryptkey_dup(k);
3188 matches_endp = &tmp->next;
3189 the_valid_key = tmp;
3192 key_list_wipe(&keys);
3195 if (the_valid_key && !multi && !weak
3196 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3198 * There was precisely one strong match on a valid ID, there
3199 * were no valid keys with weak matches, and we aren't
3200 * interested in seeing invalid keys.
3202 * Proceed without asking the user.
3204 k = cryptkey_dup(the_valid_key);
3207 * Else: Ask the user.
3209 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3211 key_list_wipe(&matches);
3221 crypt_getkeybystr(const char *p, int abilities, int app, int *forced_valid)
3224 cryptkey_t *matches = NULL;
3225 cryptkey_t **matches_endp = &matches;
3229 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3235 string_array_init(&hints);
3236 add_hints(&hints, p);
3237 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3238 string_array_wipe(&hints);
3244 for (k = keys; k; k = k->next) {
3245 const char *s = crypt_keyid(k);
3247 if (abilities && !(k->flags & abilities))
3252 if (!*p || !m_strcasecmp(p, s)
3253 || (!m_strncasecmp(p, "0x", 2) && !m_strcasecmp(p + 2, s))
3254 || m_stristr(k->uid, p))
3258 *matches_endp = tmp = cryptkey_dup(k);
3259 matches_endp = &tmp->next;
3262 key_list_wipe(&keys);
3265 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3266 key_list_wipe(&matches);
3273 /* Display TAG as a prompt to ask for a key.
3274 * ABILITIES describe the required key abilities (sign, encrypt) and APP the
3275 * type of the requested key; ether S/MIME or PGP.
3276 * Return a copy of the key or NULL if not found. */
3278 crypt_ask_for_key(const char *tag, int abilities, int app, int *forced_valid)
3285 forced_valid = &dummy;
3291 if (mutt_get_field(tag, resp, sizeof(resp), M_CLEAR) != 0)
3294 if (m_strisempty(resp))
3297 if ((key = crypt_getkeybystr(resp, abilities, app, forced_valid)))
3304 /* This routine attempts to find the keyids of the recipients of a
3305 message. It returns NULL if any of the keys can not be found. */
3306 static char *find_keys(ENVELOPE *env, unsigned int app)
3308 address_t *lst = NULL, *addr;
3309 buffer_t *keylist = buffer_new();
3312 address_t **last = &lst;
3313 *last = address_list_dup(env->to);
3314 last = address_list_last(last);
3315 *last = address_list_dup(env->cc);
3316 last = address_list_last(last);
3317 *last = address_list_dup(env->bcc);
3319 rfc822_qualify(lst, mutt_fqdn(1));
3320 address_list_uniq(lst);
3323 while ((addr = address_list_pop(&lst))) {
3325 int forced_valid = 0;
3327 cryptkey_t *key = NULL;
3329 if ((keyID = mutt_crypt_hook(addr))) {
3332 snprintf(buf, sizeof(buf), _("Use keyID = \"%s\" for %s?"), keyID,
3334 r = mutt_yesorno(buf, M_YES);
3337 address_list_wipe(&lst);
3338 address_list_wipe(&addr);
3339 buffer_delete(&keylist);
3345 /* check for e-mail address */
3346 if (strchr(keyID, '@') && (a = rfc822_parse_adrlist(NULL, keyID))) {
3347 rfc822_qualify(a, mutt_fqdn(1));
3348 address_list_wipe(&addr);
3351 key = crypt_getkeybystr(keyID, KEYFLAG_CANENCRYPT, app,
3358 key = crypt_getkeybyaddr(addr, KEYFLAG_CANENCRYPT, app, &forced_valid);
3361 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), addr->mailbox);
3362 key = crypt_ask_for_key(buf, KEYFLAG_CANENCRYPT, app,
3365 address_list_wipe(&lst);
3366 address_list_wipe(&addr);
3367 buffer_delete(&keylist);
3373 buffer_addch(keylist, ' ');
3374 buffer_addstr(keylist, "0x");
3375 buffer_addstr(keylist, crypt_fpr(key));
3377 buffer_addch(keylist, '!');
3379 key_list_wipe(&key);
3380 address_list_wipe(&addr);
3383 address_list_wipe(&lst);
3384 return buffer_unwrap(&keylist);
3387 int crypt_get_keys(HEADER *msg, char **keylist)
3389 /* Do a quick check to make sure that we can find all of the encryption
3390 * keys if the user has requested this service.
3395 if (msg->security & ENCRYPT) {
3396 if (msg->security & APPLICATION_PGP) {
3397 set_option(OPTPGPCHECKTRUST);
3398 *keylist = find_keys(msg->env, APPLICATION_PGP);
3399 unset_option(OPTPGPCHECKTRUST);
3404 if (msg->security & APPLICATION_SMIME) {
3405 *keylist = find_keys(msg->env, APPLICATION_SMIME);
3415 int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
3421 if (msg->security & APPLICATION_SMIME)
3423 if (msg->security & APPLICATION_PGP)
3427 ? mutt_multi_choice(_("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3429 : mutt_multi_choice(_("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3433 case 1: /* (e)ncrypt */
3434 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3435 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3438 case 2: /* (s)ign */
3439 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3440 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3443 case 3: /* sign (a)s */
3444 p = crypt_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN,
3445 is_smime ? APPLICATION_SMIME : APPLICATION_PGP,
3448 snprintf(buf, sizeof(buf), "0x%s", crypt_keyid(p));
3449 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs, buf);
3451 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3453 *redraw = REDRAW_FULL;
3456 case 4: /* (b)oth */
3458 msg->security = SMIMEENCRYPT | SMIMESIGN;
3460 msg->security = PGPENCRYPT | PGPSIGN;
3464 case 5: /* (p)gp or s/(m)ime */
3465 is_smime = !is_smime;
3468 case 6: /* (c)lear */
3469 return msg->security = 0;
3473 msg->security &= ~APPLICATION_PGP;
3474 msg->security |= APPLICATION_SMIME;
3476 msg->security &= ~APPLICATION_SMIME;
3477 msg->security |= APPLICATION_PGP;
3480 return msg->security;
3483 int crypt_smime_verify_sender(HEADER *h)
3485 address_t *sender = NULL;
3486 unsigned int ret = 1;
3489 h->env->from = mutt_expand_aliases(h->env->from);
3490 sender = h->env->from;
3491 } else if (h->env->sender) {
3492 h->env->sender = mutt_expand_aliases (h->env->sender);
3493 sender = h->env->sender;
3497 mutt_any_key_to_continue ("Failed to figure out sender");
3501 if (signature_key) {
3502 gpgme_key_t key = signature_key;
3503 gpgme_user_id_t uid = NULL;
3504 int sender_length = 0;
3507 sender_length = m_strlen(sender->mailbox);
3508 for (uid = key->uids; uid && ret; uid = uid->next) {
3509 uid_length = m_strlen(uid->email);
3510 if (1 && (uid->email[0] == '<')
3511 && (uid->email[uid_length - 1] == '>')
3512 && (uid_length == sender_length + 2)
3513 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3517 mutt_any_key_to_continue ("Failed to verify sender");
3521 if (signature_key) {
3522 gpgme_key_unref(signature_key);
3523 signature_key = NULL;
3528 static void crypt_invoke_import(FILE *stream, int smime)
3530 gpgme_ctx_t ctx = create_gpgme_context(smime);
3534 err = gpgme_data_new_from_stream(&data, stream);
3536 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3541 err = gpgme_op_import(ctx, data);
3543 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3544 gpgme_data_release(data);
3549 gpgme_data_release(data);
3554 static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
3557 FILE *tmpfp = tmpfile();
3559 if (tmpfp == NULL) {
3560 mutt_perror (_("Can't create temporary file"));
3567 mutt_body_handler(top, &s);
3570 crypt_invoke_import(tmpfp, 0);
3574 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3578 for (; top; top = top->next) {
3579 if (!tag || top->tagged)
3580 pgp_extract_keys_from_attachment (fp, top);
3587 void crypt_invoke_message (int type)
3589 if (type & APPLICATION_PGP) {
3590 mutt_message _("Invoking PGP...");
3592 else if (type & APPLICATION_SMIME) {
3593 mutt_message _("Invoking S/MIME...");
3597 int mutt_protect (HEADER * msg, char *keylist)
3599 BODY *pbody = NULL, *tmp_pbody = NULL;
3600 BODY *tmp_smime_pbody = NULL;
3601 BODY *tmp_pgp_pbody = NULL;
3602 int flags = msg->security;
3607 tmp_smime_pbody = msg->content;
3608 tmp_pgp_pbody = msg->content;
3610 if (msg->security & SIGN) {
3611 if (msg->security & APPLICATION_SMIME) {
3612 if (!(tmp_pbody = sign_message(msg->content, 1)))
3614 pbody = tmp_smime_pbody = tmp_pbody;
3617 if ((msg->security & APPLICATION_PGP)
3618 && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
3619 if (!(tmp_pbody = sign_message(msg->content, 0)))
3623 pbody = tmp_pgp_pbody = tmp_pbody;
3626 if ((msg->security & APPLICATION_SMIME)
3627 && (msg->security & APPLICATION_PGP)) {
3628 /* here comes the draft ;-) */
3633 if (msg->security & ENCRYPT) {
3634 if ((msg->security & APPLICATION_SMIME)) {
3635 if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
3637 /* signed ? free it! */
3640 /* free tmp_body if messages was signed AND encrypted ... */
3641 if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
3642 /* detatch and dont't delete msg->content,
3643 which tmp_smime_pbody->parts after signing. */
3644 tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
3645 msg->content->next = NULL;
3646 body_list_wipe(&tmp_smime_pbody);
3651 if ((msg->security & APPLICATION_PGP)) {
3652 if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
3655 /* did we perform a retainable signature? */
3656 if (flags != msg->security) {
3657 /* remove the outer multipart layer */
3658 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3659 /* get rid of the signature */
3660 body_list_wipe(&tmp_pgp_pbody->next);
3666 /* destroy temporary signature envelope when doing retainable
3670 if (flags != msg->security) {
3671 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3672 body_list_wipe(&tmp_pgp_pbody->next);
3678 msg->content = pbody;
3684 int crypt_query (BODY * m)
3691 if (m->type == TYPEAPPLICATION) {
3692 t |= mutt_is_application_pgp (m);
3694 t |= mutt_is_application_smime (m);
3695 if (t && m->goodsig)
3700 else if (m->type == TYPETEXT) {
3701 t |= mutt_is_application_pgp (m);
3702 if (t && m->goodsig)
3706 if (m->type == TYPEMULTIPART) {
3707 t |= mutt_is_multipart_encrypted (m);
3708 t |= mutt_is_multipart_signed (m);
3710 if (t && m->goodsig)
3714 if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
3718 u = m->parts ? ~0 : 0; /* Bits set in all parts */
3719 w = 0; /* Bits set in any part */
3721 for (p = m->parts; p; p = p->next) {
3722 v = crypt_query (p);
3726 t |= u | (w & ~GOODSIGN);
3728 if ((w & GOODSIGN) && !(u & GOODSIGN))
3736 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
3742 fseeko (s->fpin, a->hdr_offset, 0);
3743 bytes = a->length + a->offset - a->hdr_offset;
3746 if ((c = fgetc (s->fpin)) == EOF)
3754 if (c == '\n' && !hadcr)
3763 static void extract_keys_aux(FILE *fpout, HEADER *h)
3765 mutt_parse_mime_message (Context, h);
3768 if (h->security & APPLICATION_PGP) {
3769 mutt_copy_message(fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
3772 mutt_endwin (_("Trying to extract PGP keys...\n"));
3775 if (h->security & APPLICATION_SMIME) {
3776 if (h->security & ENCRYPT)
3777 mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
3778 | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
3780 mutt_copy_message(fpout, Context, h, 0, 0);
3783 mutt_message (_("Trying to extract S/MIME certificates...\n"));
3787 crypt_invoke_import(fpout, h->security & APPLICATION_SMIME);
3790 void crypt_extract_keys_from_messages(HEADER * h)
3792 FILE *tmpfp = tmpfile();
3794 mutt_error(_("Could not create temporary file"));
3800 for (i = 0; i < Context->vcount; i++) {
3801 if (!Context->hdrs[Context->v2r[i]]->tagged)
3803 extract_keys_aux(tmpfp, Context->hdrs[Context->v2r[i]]);
3806 extract_keys_aux(tmpfp, h);
3811 mutt_any_key_to_continue(NULL);
3814 static void crypt_fetch_signatures(BODY ***signatures, BODY * a, int *n)
3816 for (; a; a = a->next) {
3817 if (a->type == TYPEMULTIPART) {
3818 crypt_fetch_signatures(signatures, a->parts, n);
3821 p_realloc(signatures, *n + 6);
3823 (*signatures)[(*n)++] = a;
3828 int mutt_signed_handler(BODY *a, STATE *s)
3830 unsigned major, minor;
3832 int rc, i, goodsig = 1, sigcnt = 0;
3835 protocol = parameter_getval(a->parameter, "protocol");
3838 switch (mime_which_token(protocol, -1)) {
3839 case MIME_APPLICATION_PGP_SIGNATURE:
3840 major = TYPEAPPLICATION;
3841 minor = MIME_PGP_SIGNATURE;
3843 case MIME_APPLICATION_X_PKCS7_SIGNATURE:
3844 major = TYPEAPPLICATION;
3845 minor = MIME_X_PKCS7_SIGNATURE;
3847 case MIME_APPLICATION_PKCS7_SIGNATURE:
3848 major = TYPEAPPLICATION;
3849 minor = MIME_PKCS7_SIGNATURE;
3851 case MIME_MULTIPART_MIXED:
3852 major = TYPEMULTIPART;
3857 state_printf(s, _("[-- Error: "
3858 "Unknown multipart/signed protocol %s! --]\n\n"),
3860 return mutt_body_handler (a, s);
3863 /* consistency check */
3864 if (!(a && a->next && a->next->type == major &&
3865 mime_which_token(a->next->subtype, -1) == minor))
3867 state_attach_puts(_("[-- Error: "
3868 "Inconsistent multipart/signed structure! --]\n\n"),
3870 return mutt_body_handler (a, s);
3873 if (s->flags & M_DISPLAY) {
3876 crypt_fetch_signatures (&sigs, a->next, &sigcnt);
3878 FILE *tmpfp = tmpfile();
3881 mutt_error(_("Could not create temporary file"));
3883 crypt_write_signed(a, s, tmpfp);
3885 for (i = 0; i < sigcnt; i++) {
3886 if (sigs[i]->type == TYPEAPPLICATION) {
3889 switch ((subtype = mime_which_token(sigs[i]->subtype, -1))) {
3890 case MIME_PGP_SIGNATURE:
3891 case MIME_X_PKCS7_SIGNATURE:
3892 case MIME_PKCS7_SIGNATURE:
3893 if (crypt_verify_one(sigs[i], s, tmpfp, subtype != MIME_PGP_SIGNATURE) != 0)
3904 state_printf(s, _("[-- Warning: "
3905 "We can't verify %s/%s signatures. --]\n\n"),
3906 TYPE (sigs[i]), sigs[i]->subtype);
3910 b->goodsig = goodsig;
3911 b->badsig = !goodsig;
3913 /* Now display the signed body */
3914 state_attach_puts(_("[-- The following data is signed --]\n\n"), s);
3918 state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"),
3923 rc = mutt_body_handler (a, s);
3925 if (s->flags & M_DISPLAY && sigcnt)
3926 state_attach_puts (_("\n[-- End of signed data --]\n"), s);