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/lib-ui.h>
20 #include <lib-ui/menu.h>
21 #include <lib-mx/mx.h>
28 #include "recvattach.h"
31 @import "lib-lua/base.cpkg"
37 ** This variable controls whether or not Madmutt may automatically enable
38 ** S/MIME encryption/signing for messages. See also ``$$crypt_autoencrypt'',
39 ** ``$$crypt_replyencrypt'',
40 ** ``$$crypt_autosign'', ``$$crypt_replysign'' and ``$$smime_is_default''.
45 ** This variable controls whether or not Madmutt may automatically enable
46 ** PGP encryption/signing for messages. See also ``$$crypt_autoencrypt'',
47 ** ``$$crypt_replyencrypt'',
48 ** ``$$crypt_autosign'', ``$$crypt_replysign'' and ``$$smime_is_default''.
53 ** Setting this variable will cause Madmutt to always attempt to
54 ** cryptographically sign outgoing messages. This can be overridden
55 ** by use of the \fIpgp-menu\fP, when signing is not required or
56 ** encryption is requested as well. If ``$$smime_is_default'' is \fIset\fP,
57 ** then OpenSSL is used instead to create S/MIME messages and settings can
58 ** be overridden by use of the \fIsmime-menu\fP.
64 ** Setting this variable will cause Madmutt to always attempt to PGP
65 ** encrypt outgoing messages. This is probably only useful in
66 ** connection to the \fIsend-hook\fP command. It can be overridden
67 ** by use of the \fIpgp-menu\fP, when encryption is not required or
68 ** signing is requested as well. If ``$$smime_is_default'' is \fIset\fP,
69 ** then OpenSSL is used instead to create S/MIME messages and
70 ** settings can be overridden by use of the \fIsmime-menu\fP.
73 bool replyencrypt = 1;
76 ** If \fIset\fP, automatically PGP or OpenSSL encrypt replies to messages which are
83 ** If \fIset\fP, automatically PGP or OpenSSL sign replies to messages which are
86 ** \fBNote:\fP this does not work on messages that are encrypted \fBand\fP signed!
89 bool replysignencrypted = 1;
92 ** If \fIset\fP, automatically PGP or OpenSSL sign replies to messages
93 ** which are encrypted. This makes sense in combination with
94 ** ``$$crypt_replyencrypt'', because it allows you to sign all
95 ** messages which are automatically encrypted. This works around
96 ** the problem noted in ``$$crypt_replysign'', that Madmutt is not able
97 ** to find out whether an encrypted message is also signed.
100 bool smime_is_default = 0;
103 ** The default behaviour of Madmutt is to use PGP on all auto-sign/encryption
104 ** operations. To override and to use OpenSSL instead this must be \fIset\fP.
106 ** However, this has no effect while replying, since Madmutt will automatically
107 ** select the same application that was used to sign/encrypt the original
110 ** (Note that this variable can be overridden by unsetting $$crypt_autosmime.)
113 quadopt_t verify_sig = M_YES;
116 ** If ``\fIyes\fP'', always attempt to verify PGP or S/MIME signatures.
117 ** If ``\fIask\fP'', ask whether or not to verify the signature.
118 ** If ``\fIno\fP'', never attempt to verify cryptographic signatures.
122 string_t pgp_entry_format = m_strdup("%4n %t%f %4l/0x%k %-4a %2c %u");
125 ** This variable allows you to customize the PGP key selection menu to
126 ** your personal taste. This string is similar to ``$$index_format'', but
127 ** has its own set of \fTprintf(3)\fP-like sequences:
132 ** .dt %u .dd user id
133 ** .dt %a .dd algorithm
134 ** .dt %l .dd key length
136 ** .dt %c .dd capabilities
137 ** .dt %t .dd trust/validity of the key-uid association
138 ** .dt %[<s>] .dd date of the key where <s> is an \fTstrftime(3)\fP expression
145 /* Values used for comparing addresses. */
146 #define CRYPT_KV_VALID 1
147 #define CRYPT_KV_ADDR 2
148 #define CRYPT_KV_STRING 4
149 #define CRYPT_KV_STRONGID 8
150 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
157 /* We work based on user IDs, getting from a user ID to the key is
158 check and does not need any memory (gpgme uses reference counting). */
159 typedef struct cryptkey_t {
160 struct cryptkey_t *next;
161 int idx; /* and the user ID at this index */
162 int flags; /* global and per uid flags (for convenience) */
164 const char *uid; /* and for convenience point to this user ID */
167 DO_INIT(cryptkey_t, cryptkey);
168 static void cryptkey_wipe(cryptkey_t *key) {
169 gpgme_key_release(key->kobj);
171 DO_NEW(cryptkey_t, cryptkey);
172 DO_DELETE(cryptkey_t, cryptkey);
173 DO_SLIST(cryptkey_t, key, cryptkey_delete);
175 static cryptkey_t *cryptkey_dup(const cryptkey_t *k)
177 cryptkey_t *res = cryptkey_new();
180 gpgme_key_ref(k->kobj);
184 typedef struct crypt_entry {
189 static gpgme_key_t signature_key = NULL;
191 static void convert_to_7bit (BODY * a)
193 for (; a; a = a->next) {
194 int tok = mime_which_token(a->subtype, -1);
196 if (a->type == TYPEMULTIPART) {
197 a->encoding = ENC7BIT;
198 convert_to_7bit(a->parts);
199 } else if (a->type == TYPEMESSAGE && tok != MIME_DELIVERY_STATUS) {
200 if (a->encoding != ENC7BIT)
201 mutt_message_to_7bit(a, NULL);
202 } else if (a->encoding == ENC8BIT) {
203 a->encoding = ENCQUOTEDPRINTABLE;
204 } else if (a->encoding == ENCBINARY) {
205 a->encoding = ENCBASE64;
206 } else if (a->content && a->encoding != ENCBASE64
207 && (a->content->from || a->content->space))
209 a->encoding = ENCQUOTEDPRINTABLE;
214 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
215 FP. Convert the character set. */
216 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
220 tstr = p_dupstr(buf, len);
221 mutt_convert_string(&tstr, "utf-8", mod_cset.charset, M_ICONV_HOOK_FROM);
226 /* Return the keyID for the key K. Note that this string is valid as
227 long as K is valid */
228 static const char *crypt_keyid (cryptkey_t * k)
230 if (k->kobj && k->kobj->subkeys) {
231 const char *s = k->kobj->subkeys->keyid;
232 return m_strlen(s) == 16 ? s + 8 : s;
238 /* Return the hexstring fingerprint from the key K. */
239 static const char *crypt_fpr (cryptkey_t * k)
241 return k->kobj && k->kobj->subkeys ? k->kobj->subkeys->fpr : "";
244 /* Parse FLAGS and return a statically allocated(!) string with them. */
245 static char *crypt_key_abilities (int flags)
247 static char buff[3] = "es";
249 if (!(flags & KEYFLAG_CANENCRYPT))
251 else if (flags & KEYFLAG_PREFER_SIGNING)
254 if (!(flags & KEYFLAG_CANSIGN))
256 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
262 /* Parse FLAGS and return a character describing the most important flag. */
263 static char crypt_flags(int flags)
265 if (flags & KEYFLAG_REVOKED)
267 if (flags & KEYFLAG_EXPIRED)
269 if (flags & KEYFLAG_DISABLED)
271 if (flags & KEYFLAG_CRITICAL)
276 /* Return true whe validity of KEY is sufficient. */
277 static int crypt_id_is_strong (cryptkey_t * key)
282 if (key->flags & KEYFLAG_ISX509)
285 for (i = 0, uid = key->kobj->uids; uid; i++, uid = uid->next) {
287 return uid->validity == GPGME_VALIDITY_FULL
288 || uid->validity == GPGME_VALIDITY_ULTIMATE;
295 /* Return a bit vector describing how well the addresses ADDR and
296 U_ADDR match and whether KEY is valid. */
298 crypt_id_matches_addr(address_t *addr, address_t *u_addr, cryptkey_t *key)
302 if (!(key->flags & KEYFLAG_CANTUSE))
303 rv |= CRYPT_KV_VALID;
305 if (crypt_id_is_strong(key))
306 rv |= CRYPT_KV_STRONGID;
308 if (addr->mailbox && !m_strcasecmp(addr->mailbox, u_addr->mailbox))
311 if (addr->personal && m_strcasecmp(addr->personal, u_addr->personal))
312 rv |= CRYPT_KV_STRING;
318 /* Create a new gpgme context and return it. With FOR_SMIME set to
319 true, the protocol of the context is set to CMS. */
320 static gpgme_ctx_t create_gpgme_context(int for_smime)
325 err = gpgme_new (&ctx);
327 mutt_error(_("error creating gpgme context: %s\n"),
328 gpgme_strerror(err));
335 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
337 mutt_error(_("error enabling CMS protocol: %s\n"), gpgme_strerror(err));
344 /* Create a new gpgme data object. This is a wrapper to die on
346 static gpgme_data_t create_gpgme_data(void)
351 err = gpgme_data_new(&data);
353 mutt_error(_("error creating gpgme data object: %s\n"),
354 gpgme_strerror(err));
361 /* Create a new GPGME Data object from the mail body A. With CONVERT
362 passed as true, the lines are converted to CR,LF if required.
363 Return NULL on error or the gpgme_data_t object on success. */
364 static gpgme_data_t body_to_data_object(BODY *a, int convert)
370 if (!(fptmp = tmpfile())) {
371 mutt_perror (_("Can't create temporary file"));
375 mutt_write_mime_header(a, fptmp);
377 mutt_write_mime_body(a, fptmp);
384 data = create_gpgme_data();
386 while (fgets(buf + spare, sizeof(buf) - 1, fptmp)) {
387 int l = m_strlen(buf);
389 spare = buf[l - 1] != '\n';
390 if (!spare && (l <= 1 || buf[l - 2] != '\r')) {
394 gpgme_data_write(data, buf, l - spare);
399 gpgme_data_write(data, buf, 1);
400 gpgme_data_seek(data, 0, SEEK_SET);
405 err = gpgme_data_new_from_stream(&data, fptmp);
408 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
414 /* Create a GPGME data object from the stream FP but limit the object
415 to LENGTH bytes starting at OFFSET bytes from the beginning of the
417 static gpgme_data_t file_to_data_object(FILE *fp, long offset, long length)
422 err = gpgme_data_new_from_filepart(&data, NULL, fp, offset, length);
424 mutt_error(_("error allocating data object: %s\n"),
425 gpgme_strerror(err));
432 /* Write a GPGME data object to the stream FP. */
433 static int data_object_to_stream(gpgme_data_t data, FILE *fp)
439 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
440 ? gpgme_error_from_errno (errno) : 0);
442 mutt_error (_("error rewinding data object: %s\n"),
443 gpgme_strerror(err));
447 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
448 /* fixme: we are not really converting CRLF to LF but just
449 skipping CR. Doing it correctly needs a more complex logic */
450 for (p = buf; nread; p++, nread--) {
456 mutt_perror ("[tempfile]");
461 mutt_error(_("error reading data object: %s\n"), strerror(errno));
467 /* Copy a data object to a newly created temporay file and return that
468 filename. Caller must free. With RET_FP not NULL, don't close the
469 stream but return it there. */
470 static char *data_object_to_tempfile(gpgme_data_t data, FILE **ret_fp)
473 char tempfile[_POSIX_PATH_MAX], buf[BUFSIZ];
477 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
479 mutt_perror (_("Can't create temporary file"));
483 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
484 ? gpgme_error_from_errno (errno) : 0);
486 mutt_perror(_("Can't create temporary file"));
490 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
491 if (fwrite (buf, nread, 1, fp) != 1) {
492 mutt_perror (_("Can't create temporary file"));
498 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
507 return m_strdup(tempfile);
516 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
518 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
524 if (!ctx || !r_key || !fpr)
525 return gpg_error (GPG_ERR_INV_VALUE);
527 if (strlen (fpr) < 8) /* We have at least a key ID. */
528 return gpg_error (GPG_ERR_INV_VALUE);
530 /* FIXME: We use our own context because we have to avoid the user's
531 I/O callback handlers. */
532 err = gpgme_new (&listctx);
535 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
536 err = gpgme_op_keylist_start (listctx, fpr, secret);
538 err = gpgme_op_keylist_next (listctx, r_key);
539 gpgme_release (listctx);
543 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
544 The keys must be space delimited. */
545 static gpgme_key_t *create_recipient_set(const char *s, int smime)
547 gpgme_ctx_t ctx = create_gpgme_context(smime);
548 gpgme_key_t *rset = NULL;
555 const char *p = m_strnextsp(s);
558 m_strncpy(buf, sizeof(buf), s, p - s);
559 if (p - s > 1 && p[-1] == '!') {
560 /* user wants to override the valididy of that key. */
562 buf[p - s - 1] = '\0';
563 err = gpgme_get_key2(ctx, buf, &key, 0);
565 key->uids->validity = GPGME_VALIDITY_FULL;
567 err = gpgme_get_key2(ctx, buf, &key, 0);
571 mutt_error(_("error adding recipient `%.*s': %s\n"),
572 (int)(p - s), s, gpgme_strerror(err));
577 p_realloc(&rset, rset_n + 1);
578 rset[rset_n++] = key;
583 /* NULL terminate. */
584 p_realloc(&rset, rset_n + 1);
585 rset[rset_n++] = NULL;
593 /* Make sure that the correct signer is set. Returns 0 on success. */
594 static int set_signer(gpgme_ctx_t ctx, int for_smime)
596 const char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
600 if (m_strisempty(signid))
603 err = gpgme_get_key(ctx, signid, &key, 1);
605 mutt_error(_("error getting secret key `%s': %s\n"), signid,
606 gpgme_strerror(err));
610 gpgme_signers_clear(ctx);
611 err = gpgme_signers_add(ctx, key);
612 gpgme_key_unref(key);
614 mutt_error(_("error setting secret key `%s': %s\n"), signid,
615 gpgme_strerror(err));
622 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
623 and return an allocated filename to a temporary file containing the
624 enciphered text. With USE_SMIME set to true, the smime backend is
625 used. With COMBINED_SIGNED a PGP message is signed and
626 encrypted. Returns NULL in case of error */
627 static char *encrypt_gpgme_object(gpgme_data_t plaintext, gpgme_key_t *rset,
628 int use_smime, int combined_signed)
632 gpgme_data_t ciphertext;
635 ctx = create_gpgme_context (use_smime);
637 gpgme_set_armor (ctx, 1);
639 ciphertext = create_gpgme_data ();
641 if (combined_signed) {
642 if (set_signer(ctx, use_smime)) {
643 gpgme_data_release(ciphertext);
647 err = gpgme_op_encrypt_sign(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
648 plaintext, ciphertext);
650 err = gpgme_op_encrypt(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
651 plaintext, ciphertext);
654 mutt_need_hard_redraw();
656 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
657 gpgme_data_release (ciphertext);
664 outfile = data_object_to_tempfile(ciphertext, NULL);
665 gpgme_data_release(ciphertext);
669 /* Find the "micalg" parameter from the last Gpgme operation on
670 context CTX. It is expected that this operation was a sign
671 operation. Return the algorithm name as a C string in buffer BUF
672 which must have been allocated by the caller with size BUFLEN.
673 Returns 0 on success or -1 in case of an error. The return string
674 is truncted to BUFLEN - 1. */
675 static int get_micalg(gpgme_ctx_t ctx, char *buf, ssize_t buflen)
677 gpgme_sign_result_t result = NULL;
678 const char *alg = NULL;
680 result = gpgme_op_sign_result(ctx);
681 if (result && result->signatures) {
682 alg = gpgme_hash_algo_name(result->signatures->hash_algo);
684 m_strcpy(buf, buflen, NONULL(alg));
689 static void print_time(time_t t, STATE *s)
693 setlocale(LC_TIME, "");
695 strftime(p, sizeof(p), nl_langinfo(D_T_FMT), localtime(&t));
697 strftime(p, sizeof(p), "%c", localtime(&t));
699 setlocale(LC_TIME, "C");
700 state_attach_puts(p, s);
703 /* Implementation of `sign_message'. */
705 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
706 USE_SMIME is passed as true. Returns the new body or NULL on
708 static BODY *sign_message(BODY * a, int use_smime)
715 gpgme_data_t message, signature;
717 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
719 message = body_to_data_object(a, 1);
722 signature = create_gpgme_data ();
724 ctx = create_gpgme_context (use_smime);
726 gpgme_set_armor (ctx, 1);
728 if (set_signer (ctx, use_smime)) {
729 gpgme_data_release (signature);
734 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
735 mutt_need_hard_redraw ();
736 gpgme_data_release (message);
738 gpgme_data_release (signature);
740 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
744 sigfile = data_object_to_tempfile(signature, NULL);
745 gpgme_data_release (signature);
752 t->type = TYPEMULTIPART;
753 t->subtype = m_strdup("signed");
754 t->encoding = ENC7BIT;
756 t->disposition = DISPINLINE;
758 parameter_set_boundary(&t->parameter);
759 parameter_setval(&t->parameter, "protocol",
760 use_smime ? "application/pkcs7-signature"
761 : "application/pgp-signature");
762 /* Get the micalg from gpgme. Old gpgme versions don't support this
763 for S/MIME so we assume sha-1 in this case. */
764 if (!get_micalg (ctx, buf, sizeof buf))
765 parameter_setval(&t->parameter, "micalg", buf);
767 parameter_setval(&t->parameter, "micalg", "sha1");
773 t->parts->next = body_new();
775 t->type = TYPEAPPLICATION;
777 t->subtype = m_strdup("pkcs7-signature");
778 parameter_setval(&t->parameter, "name", "smime.p7s");
779 t->encoding = ENCBASE64;
781 t->disposition = DISPATTACH;
782 t->d_filename = m_strdup("smime.p7s");
785 t->subtype = m_strdup("pgp-signature");
787 t->disposition = DISPINLINE;
788 t->encoding = ENC7BIT;
790 t->filename = sigfile;
791 t->unlink = 1; /* ok to remove this file after sending. */
796 /* Encrypt the mail body A to all keys given as space separated keyids
797 or fingerprints in KEYLIST and return the encrypted body. */
798 static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
800 char *outfile = NULL;
802 gpgme_key_t *rset = NULL;
803 gpgme_data_t plaintext;
805 rset = create_recipient_set(keylist, 0);
811 plaintext = body_to_data_object(a, 0);
817 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
818 gpgme_data_release (plaintext);
824 t->type = TYPEMULTIPART;
825 t->subtype = m_strdup("encrypted");
826 t->encoding = ENC7BIT;
828 t->disposition = DISPINLINE;
830 parameter_set_boundary(&t->parameter);
831 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
833 t->parts = body_new();
834 t->parts->type = TYPEAPPLICATION;
835 t->parts->subtype = m_strdup("pgp-encrypted");
836 t->parts->encoding = ENC7BIT;
838 t->parts->next = body_new();
839 t->parts->next->type = TYPEAPPLICATION;
840 t->parts->next->subtype = m_strdup("octet-stream");
841 t->parts->next->encoding = ENC7BIT;
842 t->parts->next->filename = outfile;
843 t->parts->next->use_disp = 1;
844 t->parts->next->disposition = DISPINLINE;
845 t->parts->next->unlink = 1; /* delete after sending the message */
846 t->parts->next->d_filename = m_strdup("msg.asc");
851 /* Encrypt the mail body A to all keys given as space separated
852 fingerprints in KEYLIST and return the S/MIME encrypted body. */
853 static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
855 char *outfile = NULL;
857 gpgme_key_t *rset = NULL;
858 gpgme_data_t plaintext;
860 rset = create_recipient_set(keylist, 1);
864 plaintext = body_to_data_object(a, 0);
870 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
871 gpgme_data_release (plaintext);
877 t->type = TYPEAPPLICATION;
878 t->subtype = m_strdup("pkcs7-mime");
879 parameter_setval(&t->parameter, "name", "smime.p7m");
880 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
881 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
883 t->disposition = DISPATTACH;
884 t->d_filename = m_strdup("smime.p7m");
885 t->filename = outfile;
886 t->unlink = 1; /*delete after sending the message */
893 /* Display the common attributes of the signature summary SUM.
894 Return 1 if there is is a severe warning.
896 static int show_sig_summary (unsigned long sum,
897 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
902 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
903 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
907 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
908 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
911 state_attach_puts (_("Warning: The key used to create the "
912 "signature expired at: "), s);
914 state_attach_puts ("\n", s);
917 state_attach_puts (_("Warning: At least one certification key "
918 "has expired\n"), s);
921 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
922 gpgme_verify_result_t result;
923 gpgme_signature_t sig;
926 result = gpgme_op_verify_result (ctx);
928 for (sig = result->signatures, i = 0; sig && (i < idx);
929 sig = sig->next, i++);
931 state_attach_puts (_("Warning: The signature expired at: "), s);
932 print_time (sig ? sig->exp_timestamp : 0, s);
933 state_attach_puts ("\n", s);
936 if ((sum & GPGME_SIGSUM_KEY_MISSING))
937 state_attach_puts (_("Can't verify due to a missing "
938 "key or certificate\n"), s);
940 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
941 state_attach_puts (_("The CRL is not available\n"), s);
945 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
946 state_attach_puts (_("Available CRL is too old\n"), s);
950 if ((sum & GPGME_SIGSUM_BAD_POLICY))
951 state_attach_puts (_("A policy requirement was not met\n"), s);
953 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
954 const char *t0 = NULL, *t1 = NULL;
955 gpgme_verify_result_t result;
956 gpgme_signature_t sig;
959 state_attach_puts (_("A system error occurred"), s);
961 /* Try to figure out some more detailed system error information. */
962 result = gpgme_op_verify_result (ctx);
963 for (sig = result->signatures, i = 0; sig && (i < idx);
964 sig = sig->next, i++);
967 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
971 state_attach_puts (": ", s);
973 state_attach_puts (t0, s);
974 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
976 state_attach_puts (",", s);
977 state_attach_puts (t1, s);
980 state_attach_puts ("\n", s);
987 static void show_fingerprint (gpgme_key_t key, STATE * state)
992 const char *prefix = _("Fingerprint: ");
997 s = key->subkeys ? key->subkeys->fpr : NULL;
1000 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
1002 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
1003 buf = p_new(char, bufsize);
1004 m_strcpy(buf, bufsize, prefix);
1005 p = buf + m_strlen(buf);
1006 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
1007 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
1018 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
1021 *p++ = is_pgp ? ' ' : ':';
1022 if (is_pgp && i == 7)
1027 /* just in case print remaining odd digits */
1032 state_attach_puts (buf, state);
1036 /* Show the valididy of a key used for one signature. */
1037 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
1039 gpgme_verify_result_t result = NULL;
1040 gpgme_signature_t sig = NULL;
1041 const char *txt = NULL;
1043 result = gpgme_op_verify_result (ctx);
1045 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
1047 switch (sig ? sig->validity : 0) {
1048 case GPGME_VALIDITY_UNKNOWN:
1049 txt = _("WARNING: We have NO indication whether "
1050 "the key belongs to the person named " "as shown above\n");
1052 case GPGME_VALIDITY_UNDEFINED:
1054 case GPGME_VALIDITY_NEVER:
1055 txt = _("WARNING: The key does NOT BELONG to "
1056 "the person named as shown above\n");
1058 case GPGME_VALIDITY_MARGINAL:
1059 txt = _("WARNING: It is NOT certain that the key "
1060 "belongs to the person named as shown above\n");
1062 case GPGME_VALIDITY_FULL:
1063 case GPGME_VALIDITY_ULTIMATE:
1068 state_attach_puts (txt, s);
1071 /* Show information about one signature. This fucntion is called with
1072 the context CTX of a sucessful verification operation and the
1073 enumerator IDX which should start at 0 and incremete for each
1076 Return values are: 0 for normal procession, 1 for a bad signature,
1077 2 for a signature with a warning or -1 for no more signature. */
1078 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
1081 const char *fpr, *uid;
1082 gpgme_key_t key = NULL;
1083 int i, anybad = 0, anywarn = 0;
1085 gpgme_user_id_t uids = NULL;
1086 gpgme_verify_result_t result;
1087 gpgme_signature_t sig;
1088 gpgme_error_t err = GPG_ERR_NO_ERROR;
1090 result = gpgme_op_verify_result (ctx);
1092 /* FIXME: this code should use a static variable and remember
1093 the current position in the list of signatures, IMHO.
1096 for (i = 0, sig = result->signatures; sig && (i < idx);
1097 i++, sig = sig->next);
1099 return -1; /* Signature not found. */
1101 if (signature_key) {
1102 gpgme_key_unref(signature_key);
1103 signature_key = NULL;
1106 created = sig->timestamp;
1110 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1113 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1115 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1117 signature_key = key;
1120 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1121 error. Do it here to avoid a double free. */
1125 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1127 state_attach_puts (_("Error getting key information: "), s);
1128 state_attach_puts (gpg_strerror (err), s);
1129 state_attach_puts ("\n", s);
1132 else if ((sum & GPGME_SIGSUM_GREEN)) {
1133 state_attach_puts (_("Good signature from: "), s);
1134 state_attach_puts (uid, s);
1135 state_attach_puts ("\n", s);
1136 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1138 /* Skip primary UID. */
1142 state_attach_puts (_(" aka: "), s);
1143 state_attach_puts (uids->uid, s);
1144 state_attach_puts ("\n", s);
1146 state_attach_puts (_(" created: "), s);
1147 print_time (created, s);
1148 state_attach_puts ("\n", s);
1149 if (show_sig_summary (sum, ctx, key, idx, s))
1151 show_one_sig_validity (ctx, idx, s);
1153 else if ((sum & GPGME_SIGSUM_RED)) {
1154 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1155 state_attach_puts (uid, s);
1156 state_attach_puts ("\n", s);
1157 show_sig_summary (sum, ctx, key, idx, s);
1159 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1160 signature, so we display what a PGP user expects: The name,
1161 fingerprint and the key validity (which is neither fully or
1163 state_attach_puts (_("Good signature from: "), s);
1164 state_attach_puts (uid, s);
1165 state_attach_puts ("\n", s);
1166 state_attach_puts (_(" created: "), s);
1167 print_time (created, s);
1168 state_attach_puts ("\n", s);
1169 show_one_sig_validity (ctx, idx, s);
1170 show_fingerprint (key, s);
1171 if (show_sig_summary (sum, ctx, key, idx, s))
1174 else { /* can't decide (yellow) */
1176 state_attach_puts (_("Error checking signature"), s);
1177 state_attach_puts ("\n", s);
1178 show_sig_summary (sum, ctx, key, idx, s);
1181 if (key != signature_key)
1182 gpgme_key_unref(key);
1185 return anybad ? 1 : anywarn ? 2 : 0;
1188 /* Do the actual verification step. With IS_SMIME set to true we
1189 assume S/MIME (surprise!) */
1190 static int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
1196 gpgme_data_t signature, message;
1198 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1202 /* We need to tell gpgme about the encoding because the backend can't
1203 auto-detect plain base-64 encoding which is used by S/MIME. */
1205 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1207 err = gpgme_data_new_from_stream(&message, fp);
1209 gpgme_data_release (signature);
1210 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1213 ctx = create_gpgme_context (is_smime);
1215 /* Note: We don't need a current time output because GPGME avoids
1216 such an attack by separating the meta information from the
1218 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1220 err = gpgme_op_verify (ctx, signature, message, NULL);
1224 snprintf (buf, sizeof (buf) - 1,
1225 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1226 state_attach_puts (buf, s);
1228 else { /* Verification succeeded, see what the result is. */
1232 if (signature_key) {
1233 gpgme_key_unref(signature_key);
1234 signature_key = NULL;
1237 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1248 gpgme_verify_result_t result;
1249 gpgme_sig_notation_t notation;
1250 gpgme_signature_t sig;
1252 result = gpgme_op_verify_result (ctx);
1254 for (sig = result->signatures; sig; sig = sig->next) {
1255 if (sig->notations) {
1256 state_attach_puts ("*** Begin Notation (signature by: ", s);
1257 state_attach_puts (sig->fpr, s);
1258 state_attach_puts (") ***\n", s);
1259 for (notation = sig->notations; notation; notation = notation->next)
1261 if (notation->name) {
1262 state_attach_puts (notation->name, s);
1263 state_attach_puts ("=", s);
1265 if (notation->value) {
1266 state_attach_puts (notation->value, s);
1267 if (!(*notation->value
1268 && (notation->value[m_strlen(notation->value) - 1] ==
1270 state_attach_puts ("\n", s);
1273 state_attach_puts ("*** End Notation ***\n", s);
1279 gpgme_release (ctx);
1281 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1283 return badsig ? 1 : anywarn ? 2 : 0;
1286 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1287 IS_SMIME) with body A described further by state S. Write
1288 plaintext out to file FPOUT and return a new body. For PGP returns
1289 a flag in R_IS_SIGNED to indicate whether this is a combined
1290 encrypted and signed message, for S/MIME it returns true when it is
1291 not a encrypted but a signed message. */
1293 decrypt_part(BODY *a, STATE *s, FILE *fpout, int is_smime, int *r_is_signed)
1299 gpgme_data_t ciphertext, plaintext;
1300 int maybe_signed = 0;
1307 ctx = create_gpgme_context (is_smime);
1310 /* Make a data object from the body, create context etc. */
1311 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1314 plaintext = create_gpgme_data ();
1316 /* Do the decryption or the verification in case of the S/MIME hack. */
1317 if ((!is_smime) || maybe_signed) {
1319 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1320 else if (maybe_signed)
1321 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1324 /* Check wether signatures have been verified. */
1325 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1327 if (verify_result->signatures)
1332 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1333 gpgme_data_release (ciphertext);
1335 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1336 /* Check whether this might be a signed message despite what
1337 the mime header told us. Retry then. gpgsm returns the
1338 error information "unsupported Algorithm '?'" but gpgme
1339 will not store this unknown algorithm, thus we test that
1340 it has not been set. */
1341 gpgme_decrypt_result_t result;
1343 result = gpgme_op_decrypt_result (ctx);
1344 if (!result->unsupported_algorithm) {
1346 gpgme_data_release (plaintext);
1350 mutt_need_hard_redraw ();
1351 if ((s->flags & M_DISPLAY)) {
1354 snprintf (buf, sizeof (buf) - 1,
1355 _("[-- Error: decryption failed: %s --]\n\n"),
1356 gpgme_strerror (err));
1357 state_attach_puts (buf, s);
1359 gpgme_data_release (plaintext);
1360 gpgme_release (ctx);
1363 mutt_need_hard_redraw ();
1365 /* Read the output from GPGME, and make sure to change CRLF to LF,
1366 otherwise read_mime_header has a hard time parsing the message. */
1367 if (data_object_to_stream (plaintext, fpout)) {
1368 gpgme_data_release (plaintext);
1369 gpgme_release (ctx);
1372 gpgme_data_release (plaintext);
1374 a->is_signed_data = 0;
1380 a->is_signed_data = 1;
1382 *r_is_signed = -1; /* A signature exists. */
1384 if ((s->flags & M_DISPLAY))
1385 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1386 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1392 if (!anybad && idx && r_is_signed && *r_is_signed)
1393 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1395 if ((s->flags & M_DISPLAY))
1396 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1398 gpgme_release (ctx);
1403 tattach = mutt_read_mime_header (fpout, 0);
1406 * Need to set the length of this body part.
1408 fstat (fileno (fpout), &info);
1409 tattach->length = info.st_size - tattach->offset;
1411 tattach->warnsig = anywarn;
1413 /* See if we need to recurse on this MIME part. */
1414 mutt_parse_part (fpout, tattach);
1420 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1421 the stream in CUR and FPOUT. Returns 0 on success. */
1422 int crypt_pgp_decrypt_mime (FILE * fpin, FILE **fpout, BODY *b, BODY **cur)
1425 BODY *first_part = b;
1428 first_part->goodsig = 0;
1429 first_part->warnsig = 0;
1431 if (!mutt_is_multipart_encrypted(b) || !b->parts || !b->parts->next)
1440 mutt_perror (_("Can't create temporary file"));
1444 *cur = decrypt_part(b, &s, *fpout, 0, &is_signed);
1446 first_part->goodsig = is_signed > 0;
1447 return *cur ? 0 : -1;
1451 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1452 the stream in CUR and FPOUT. Returns 0 on success. */
1453 int crypt_smime_decrypt_mime(FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
1458 long saved_b_offset;
1459 ssize_t saved_b_length;
1462 if (!mutt_is_application_smime (b))
1468 /* Decode the body - we need to pass binary CMS to the
1469 backend. The backend allows for Base64 encoded data but it does
1470 not allow for QP which I have seen in some messages. So better
1472 saved_b_type = b->type;
1473 saved_b_offset = b->offset;
1474 saved_b_length = b->length;
1477 fseeko (s.fpin, b->offset, 0);
1480 mutt_perror (_("Can't create temporary file"));
1485 mutt_decode_attachment (b, &s);
1487 b->length = ftello (s.fpout);
1496 mutt_perror (_("Can't create temporary file"));
1500 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1502 (*cur)->goodsig = is_signed > 0;
1503 b->type = saved_b_type;
1504 b->length = saved_b_length;
1505 b->offset = saved_b_offset;
1508 if (*cur && !is_signed && !(*cur)->parts
1509 && mutt_is_application_smime (*cur)) {
1510 /* Assume that this is a opaque signed s/mime message. This is
1511 an ugly way of doing it but we have anyway a problem with
1512 arbitrary encoded S/MIME messages: Only the outer part may be
1513 encrypted. The entire mime parsing should be revamped,
1514 probably by keeping the temportary files so that we don't
1515 need to decrypt them all the time. Inner parts of an
1516 encrypted part can then pint into this file and tehre won't
1517 never be a need to decrypt again. This needs a partial
1518 rewrite of the MIME engine. */
1522 saved_b_type = bb->type;
1523 saved_b_offset = bb->offset;
1524 saved_b_length = bb->length;
1527 fseeko (s.fpin, bb->offset, 0);
1530 mutt_perror (_("Can't create temporary file"));
1535 mutt_decode_attachment (bb, &s);
1537 bb->length = ftello (s.fpout);
1547 mutt_perror (_("Can't create temporary file"));
1551 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1553 tmp_b->goodsig = is_signed > 0;
1554 bb->type = saved_b_type;
1555 bb->length = saved_b_length;
1556 bb->offset = saved_b_offset;
1559 body_list_wipe(cur);
1562 return *cur ? 0 : -1;
1567 pgp_check_traditional_one_body(FILE *fp, BODY *b, int tagged_only)
1569 char tempfile[_POSIX_PATH_MAX];
1570 char buf[HUGE_STRING];
1577 if (b->type != TYPETEXT)
1580 if (tagged_only && !b->tagged)
1583 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
1584 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1589 if ((tfp = fopen(tempfile, "r")) == NULL) {
1594 while (fgets (buf, sizeof (buf), tfp)) {
1595 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1596 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1598 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1608 /* fix the content type */
1610 parameter_setval(&b->parameter, "format", "fixed");
1611 parameter_setval(&b->parameter, "x-action",
1612 enc ? "pgp-encrypted" : "pgp-signed");
1616 int crypt_pgp_check_traditional(FILE *fp, BODY *b, int tagged_only)
1620 for (; b; b = b->next) {
1621 if (is_multipart(b))
1622 rv |= crypt_pgp_check_traditional(fp, b->parts, tagged_only);
1623 if (b->type == TYPETEXT) {
1625 if ((r = mutt_is_application_pgp(b))) {
1628 rv |= pgp_check_traditional_one_body(fp, b, tagged_only);
1637 Copy a clearsigned message, and strip the signature and PGP's
1640 XXX - charset handling: We assume that it is safe to do
1641 character set decoding first, dash decoding second here, while
1642 we do it the other way around in the main handler.
1644 (Note that we aren't worse than Outlook & Cie in this, and also
1645 note that we can successfully handle anything produced by any
1646 existing versions of mutt.) */
1647 static void copy_clearsigned(gpgme_data_t data, STATE * s, char *charset)
1649 char buf[HUGE_STRING];
1650 short complete, armor_header;
1655 fname = data_object_to_tempfile(data, &fp);
1661 fc = fgetconv_open (fp, charset, mod_cset.charset, M_ICONV_HOOK_FROM);
1663 for (complete = 1, armor_header = 1;
1664 fgetconvs (buf, sizeof (buf), fc) != NULL;
1665 complete = strchr (buf, '\n') != NULL) {
1668 state_puts (buf, s);
1672 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1682 state_puts (s->prefix, s);
1684 if (buf[0] == '-' && buf[1] == ' ')
1685 state_puts (buf + 2, s);
1687 state_puts (buf, s);
1690 fgetconv_close (&fc);
1694 /* Support for classic_application/pgp */
1695 int crypt_pgp_application_pgp_handler(BODY *m, STATE *s)
1697 int needpass = -1, pgp_keyblock = 0;
1701 off_t last_pos, offset;
1702 char buf[HUGE_STRING];
1703 FILE *pgpout = NULL;
1705 gpgme_error_t err = 0;
1706 gpgme_data_t armored_data = NULL;
1708 short maybe_goodsig = 1;
1709 short have_any_sigs = 0;
1711 char body_charset[STRING]; /* Only used for clearsigned messages. */
1713 /* For clearsigned messages we won't be able to get a character set
1714 but we know that this may only be text thus we assume Latin-1
1716 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1717 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1719 fseeko (s->fpin, m->offset, 0);
1720 last_pos = m->offset;
1722 for (bytes = m->length; bytes > 0;) {
1723 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1726 offset = ftello (s->fpin);
1727 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1730 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1732 start_pos = last_pos;
1734 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1736 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1740 else if (!m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1745 /* XXX - we may wish to recode here */
1747 state_puts (s->prefix, s);
1748 state_puts (buf, s);
1752 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1754 /* Copy PGP material to an data container */
1755 armored_data = create_gpgme_data ();
1756 gpgme_data_write (armored_data, buf, m_strlen(buf));
1757 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1758 offset = ftello (s->fpin);
1759 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1762 gpgme_data_write (armored_data, buf, m_strlen(buf));
1764 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1766 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1767 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1772 /* Invoke PGP if needed */
1773 if (!clearsign || (s->flags & M_VERIFY)) {
1774 unsigned int sig_stat = 0;
1775 gpgme_data_t plaintext;
1778 plaintext = create_gpgme_data ();
1779 ctx = create_gpgme_context (0);
1782 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1784 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1785 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1786 /* Decrypt verify can't handle signed only messages. */
1787 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1788 ? gpgme_error_from_errno (errno) : 0;
1789 /* Must release plaintext so that we supply an
1790 uninitialized object. */
1791 gpgme_data_release (plaintext);
1792 plaintext = create_gpgme_data ();
1793 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1800 snprintf (errbuf, sizeof (errbuf) - 1,
1801 _("Error: decryption/verification failed: %s\n"),
1802 gpgme_strerror (err));
1803 state_attach_puts (errbuf, s);
1805 else { /* Decryption/Verification succeeded */
1809 /* Check wether signatures have been verified. */
1810 gpgme_verify_result_t verify_result;
1812 verify_result = gpgme_op_verify_result (ctx);
1813 if (verify_result->signatures)
1819 if ((s->flags & M_DISPLAY) && sig_stat) {
1824 state_attach_puts (_("[-- Begin signature "
1825 "information --]\n"), s);
1828 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1837 state_attach_puts (_("[-- End signature "
1838 "information --]\n\n"), s);
1841 tmpfname = data_object_to_tempfile(plaintext, &pgpout);
1844 state_attach_puts (_("Error: copy data failed\n"), s);
1848 p_delete(&tmpfname);
1851 gpgme_release (ctx);
1855 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1856 * outputs utf-8 cleartext. This may not always be true, but it
1857 * seems to be a reasonable guess.
1860 if (s->flags & M_DISPLAY) {
1862 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1863 else if (pgp_keyblock)
1864 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1866 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1870 copy_clearsigned (armored_data, s, body_charset);
1877 fc = fgetconv_open (pgpout, "utf-8", mod_cset.charset, 0);
1878 while ((c = fgetconv (fc)) != EOF) {
1880 if (c == '\n' && s->prefix)
1881 state_puts (s->prefix, s);
1883 fgetconv_close (&fc);
1886 if (s->flags & M_DISPLAY) {
1887 state_putc ('\n', s);
1889 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1890 else if (pgp_keyblock)
1891 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1893 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1901 /* XXX - we may wish to recode here */
1903 state_puts (s->prefix, s);
1904 state_puts (buf, s);
1908 m->goodsig = (maybe_goodsig && have_any_sigs);
1910 if (needpass == -1) {
1911 state_attach_puts (_("[-- Error: could not find beginning"
1912 " of PGP message! --]\n\n"), s);
1918 /* MIME handler for pgp/mime encrypted messages. */
1919 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1921 char tempfile[_POSIX_PATH_MAX];
1924 BODY *orig_body = a;
1929 if (!a || a->type != TYPEAPPLICATION || !a->subtype
1930 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
1931 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
1932 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
1933 if (s->flags & M_DISPLAY)
1934 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
1939 /* Move forward to the application/pgp-encrypted body. */
1942 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
1944 if (s->flags & M_DISPLAY)
1945 state_attach_puts (_("[-- Error: could not create temporary file! "
1950 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
1952 tattach->goodsig = is_signed > 0;
1954 if (s->flags & M_DISPLAY)
1955 state_attach_puts (is_signed ?
1957 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
1958 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
1961 FILE *savefp = s->fpin;
1964 rc = mutt_body_handler (tattach, s);
1969 * if a multipart/signed is the _only_ sub-part of a
1970 * multipart/encrypted, cache signature verification
1973 if (mutt_is_multipart_signed (tattach) && !tattach->next)
1974 orig_body->goodsig |= tattach->goodsig;
1976 if (s->flags & M_DISPLAY) {
1977 state_puts ("\n", s);
1978 state_attach_puts (is_signed ?
1980 ("[-- End of PGP/MIME signed and encrypted data --]\n")
1981 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
1984 body_list_wipe(&tattach);
1988 mutt_unlink (tempfile);
1992 /* Support for application/smime */
1993 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
1995 char tempfile[_POSIX_PATH_MAX];
2002 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2004 if (s->flags & M_DISPLAY)
2005 state_attach_puts (_("[-- Error: could not create temporary file! "
2010 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
2012 tattach->goodsig = is_signed > 0;
2014 if (s->flags & M_DISPLAY)
2015 state_attach_puts (is_signed ?
2016 _("[-- The following data is S/MIME signed --]\n\n") :
2017 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
2020 FILE *savefp = s->fpin;
2023 rc = mutt_body_handler (tattach, s);
2028 * if a multipart/signed is the _only_ sub-part of a
2029 * multipart/encrypted, cache signature verification
2032 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
2033 if (!(a->goodsig = tattach->goodsig))
2034 a->warnsig = tattach->warnsig;
2036 else if (tattach->goodsig) {
2038 a->warnsig = tattach->warnsig;
2041 if (s->flags & M_DISPLAY) {
2042 state_puts ("\n", s);
2043 state_attach_puts (is_signed ?
2044 _("[-- End of S/MIME signed data --]\n") :
2045 _("[-- End of S/MIME encrypted data --]\n"), s);
2048 body_list_wipe(&tattach);
2052 mutt_unlink (tempfile);
2058 * Format an entry on the CRYPT key selection menu.
2061 * %k key id %K key id of the principal key
2063 * %a algorithm %A algorithm of the princ. key
2064 * %l length %L length of the princ. key
2065 * %f flags %F flags of the princ. key
2066 * %c capabilities %C capabilities of the princ. key
2067 * %t trust/validity of the key-uid association
2069 * %[...] date of key using strftime(3)
2073 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
2074 const char *src, const char *prefix,
2075 const char *ifstr, const char *elstr,
2076 anytype data, format_flag flags)
2079 crypt_entry_t *entry;
2082 int optional = (flags & M_FORMAT_OPTIONAL);
2083 const char *s = NULL;
2089 /* if (isupper ((unsigned char) op)) */
2092 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
2095 switch (ascii_tolower (op)) {
2099 char buf2[STRING], *p;
2115 while (len > 0 && *cp != ']') {
2124 break; /* not enough space */
2134 if (do_locales && Locale)
2135 setlocale (LC_TIME, Locale);
2140 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2141 tt = key->kobj->subkeys->timestamp;
2143 tm = localtime (&tt);
2145 strftime (buf2, sizeof (buf2), dest, tm);
2148 setlocale (LC_TIME, "C");
2150 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2151 snprintf (dest, destlen, fmt, buf2);
2158 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2159 snprintf (dest, destlen, fmt, entry->num);
2164 /* fixme: we need a way to distinguish between main and subkeys.
2165 Store the idx in entry? */
2166 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2167 snprintf (dest, destlen, fmt, crypt_keyid (key));
2172 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2173 snprintf (dest, destlen, fmt, key->uid);
2178 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2179 if (key->kobj->subkeys)
2180 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2183 snprintf (dest, destlen, fmt, s);
2188 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2189 if (key->kobj->subkeys)
2190 val = key->kobj->subkeys->length;
2193 snprintf (dest, destlen, fmt, val);
2198 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2199 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2201 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2206 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2207 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2209 else if (!(kflags & (KEYFLAG_ABILITIES)))
2213 if ((kflags & KEYFLAG_ISX509))
2216 gpgme_user_id_t uid = NULL;
2219 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2220 i++, uid = uid->next);
2222 switch (uid->validity) {
2223 case GPGME_VALIDITY_UNDEFINED:
2226 case GPGME_VALIDITY_NEVER:
2229 case GPGME_VALIDITY_MARGINAL:
2232 case GPGME_VALIDITY_FULL:
2235 case GPGME_VALIDITY_ULTIMATE:
2238 case GPGME_VALIDITY_UNKNOWN:
2244 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2245 snprintf (dest, destlen, fmt, s ? *s : 'B');
2248 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2249 snprintf (dest, destlen, fmt,
2250 gpgme_get_protocol_name (key->kobj->protocol));
2257 if (flags & M_FORMAT_OPTIONAL)
2258 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2259 mutt_attach_fmt, data, 0);
2263 /* Used by the display fucntion to format a line. */
2264 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2266 cryptkey_t **cryptkey_table = (cryptkey_t **) menu->data;
2267 crypt_entry_t entry;
2269 entry.key = cryptkey_table[num];
2270 entry.num = num + 1;
2272 m_strformat(s, l, getmaxx(main_w), mod_crypt.pgp_entry_format,
2273 crypt_entry_fmt, &entry, 0);
2276 /* Compare two addresses and the keyid to be used for sorting. */
2277 static int _crypt_compare_address (const void *a, const void *b)
2279 cryptkey_t **s = (cryptkey_t **) a;
2280 cryptkey_t **t = (cryptkey_t **) b;
2283 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2286 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2289 static int crypt_compare_address (const void *a, const void *b)
2291 return !(PgpSortKeys & SORT_REVERSE) == _crypt_compare_address(a, b);
2295 /* Compare two key IDs and the addresses to be used for sorting. */
2296 static int _crypt_compare_keyid (const void *a, const void *b)
2298 cryptkey_t **s = (cryptkey_t **) a;
2299 cryptkey_t **t = (cryptkey_t **) b;
2302 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2305 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2308 static int crypt_compare_keyid (const void *a, const void *b)
2310 return !(PgpSortKeys & SORT_REVERSE) == _crypt_compare_keyid(a, b);
2313 /* Compare 2 creation dates and the addresses. For sorting. */
2314 static int _crypt_compare_date (const void *a, const void *b)
2316 cryptkey_t **s = (cryptkey_t **) a;
2317 cryptkey_t **t = (cryptkey_t **) b;
2318 unsigned long ts = 0, tt = 0;
2320 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2321 ts = (*s)->kobj->subkeys->timestamp;
2322 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2323 tt = (*t)->kobj->subkeys->timestamp;
2330 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2333 static int crypt_compare_date (const void *a, const void *b)
2335 return !(PgpSortKeys & SORT_REVERSE) == _crypt_compare_date(a, b);
2338 /* Compare two trust values, the key length, the creation dates. the
2339 addresses and the key IDs. For sorting. */
2340 static int _crypt_compare_trust (const void *a, const void *b)
2342 cryptkey_t **s = (cryptkey_t **) a;
2343 cryptkey_t **t = (cryptkey_t **) b;
2344 unsigned long ts = 0, tt = 0;
2347 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2348 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2351 if ((*s)->kobj->uids)
2352 ts = (*s)->kobj->uids->validity;
2353 if ((*t)->kobj->uids)
2354 tt = (*t)->kobj->uids->validity;
2355 if ((r = (tt - ts)))
2358 if ((*s)->kobj->subkeys)
2359 ts = (*s)->kobj->subkeys->length;
2360 if ((*t)->kobj->subkeys)
2361 tt = (*t)->kobj->subkeys->length;
2365 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2366 ts = (*s)->kobj->subkeys->timestamp;
2367 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2368 tt = (*t)->kobj->subkeys->timestamp;
2374 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2376 return m_strcasecmp(crypt_keyid(*s), crypt_keyid(*t)) > 0;
2379 static int crypt_compare_trust (const void *a, const void *b)
2381 return !(PgpSortKeys & SORT_REVERSE) == _crypt_compare_trust(a, b);
2384 /* Print the X.500 Distinguished Name part KEY from the array of parts
2386 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2390 for (; dn->key; dn++) {
2391 if (!m_strcmp(dn->key, key)) {
2394 print_utf8 (fp, dn->value, m_strlen(dn->value));
2401 /* Print all parts of a DN in a standard sequence. */
2402 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2404 const char *stdpart[] = {
2405 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2407 int any = 0, any2 = 0, i;
2409 for (i = 0; stdpart[i]; i++) {
2412 any = print_dn_part (fp, dn, stdpart[i]);
2414 /* now print the rest without any specific ordering */
2415 for (; dn->key; dn++) {
2416 for (i = 0; stdpart[i]; i++) {
2417 if (!m_strcmp(dn->key, stdpart[i]))
2425 any = print_dn_part (fp, dn, dn->key);
2434 /* Parse an RDN; this is a helper to parse_dn(). */
2435 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2436 const unsigned char *string)
2438 const unsigned char *s, *s1;
2442 /* parse attributeType */
2443 for (s = string + 1; *s && *s != '='; s++);
2445 return NULL; /* error */
2448 return NULL; /* empty key */
2449 array->key = p_dupstr(string, n );
2450 p = (unsigned char *) array->key;
2453 if (*string == '#') { /* hexstring */
2455 for (s = string; hexval(*s) >= 0; s++)
2459 return NULL; /* empty or odd number of digits */
2461 p = p_new(unsigned char, n + 1);
2462 array->value = (char *) p;
2463 for (s1 = string; n; s1 += 2, n--)
2464 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2467 else { /* regular v3 quoted string */
2468 for (n = 0, s = string; *s; s++) {
2469 if (*s == '\\') { /* pair */
2471 if (*s == ',' || *s == '=' || *s == '+'
2472 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2473 || *s == '\\' || *s == '"' || *s == ' ')
2475 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2480 return NULL; /* invalid escape sequence */
2483 return NULL; /* invalid encoding */
2484 else if (*s == ',' || *s == '=' || *s == '+'
2485 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2491 p = p_new(unsigned char, n + 1);
2492 array->value = (char *) p;
2493 for (s = string; n; s++, n--) {
2496 if (hexval(*s) >= 0) {
2497 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2512 /* Parse a DN and return an array-ized one. This is not a validating
2513 parser and it does not support any old-stylish syntax; gpgme is
2514 expected to return only rfc2253 compatible strings. */
2515 static struct dn_array_s *parse_dn (const unsigned char *string)
2517 struct dn_array_s *array;
2518 ssize_t arrayidx, arraysize;
2521 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2522 array = p_new(struct dn_array_s, arraysize + 1);
2525 while (*string == ' ')
2529 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2530 struct dn_array_s *a2;
2533 a2 = p_new(struct dn_array_s, arraysize + 1);
2534 for (i = 0; i < arrayidx; i++) {
2535 a2[i].key = array[i].key;
2536 a2[i].value = array[i].value;
2541 array[arrayidx].key = NULL;
2542 array[arrayidx].value = NULL;
2543 string = parse_dn_part (array + arrayidx, string);
2547 while (*string == ' ')
2549 if (*string && *string != ',' && *string != ';' && *string != '+')
2550 goto failure; /* invalid delimiter */
2554 array[arrayidx].key = NULL;
2555 array[arrayidx].value = NULL;
2559 for (i = 0; i < arrayidx; i++) {
2560 p_delete(&array[i].key);
2561 p_delete(&array[i].value);
2568 /* Print a nice representation of the USERID and make sure it is
2569 displayed in a proper way, which does mean to reorder some parts
2570 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2571 functions. It is utf-8 encoded. */
2572 static void parse_and_print_user_id(FILE * fp, const char *userid)
2577 if (*userid == '<') {
2578 s = strchr (userid + 1, '>');
2580 print_utf8 (fp, userid + 1, s - userid - 1);
2582 else if (*userid == '(')
2583 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2584 else if (*userid & ~127 || __m_strdigits[(int)*userid] == 255)
2585 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2587 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2590 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2592 print_dn_parts (fp, dn);
2593 for (i = 0; dn[i].key; i++) {
2594 p_delete(&dn[i].key);
2595 p_delete(&dn[i].value);
2603 KEY_CAP_CAN_ENCRYPT,
2608 static unsigned int key_check_cap(gpgme_key_t key, key_cap_t cap)
2610 gpgme_subkey_t subkey = NULL;
2611 unsigned int ret = 0;
2614 case KEY_CAP_CAN_ENCRYPT:
2615 if (!(ret = key->can_encrypt))
2616 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2617 if ((ret = subkey->can_encrypt))
2620 case KEY_CAP_CAN_SIGN:
2621 if (!(ret = key->can_sign))
2622 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2623 if ((ret = subkey->can_sign))
2626 case KEY_CAP_CAN_CERTIFY:
2627 if (!(ret = key->can_certify))
2628 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2629 if ((ret = subkey->can_certify))
2638 /* Print verbose information about a key or certificate to FP. */
2639 static void print_key_info (gpgme_key_t key, FILE * fp)
2642 const char *s = NULL, *s2 = NULL;
2645 char shortbuf[STRING];
2646 unsigned long aval = 0;
2650 gpgme_user_id_t uid = NULL;
2653 setlocale (LC_TIME, Locale);
2655 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2657 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2662 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2665 fputs (_("[Invalid]"), fp);
2669 print_utf8 (fp, s, m_strlen(s));
2671 parse_and_print_user_id (fp, s);
2675 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2676 tt = key->subkeys->timestamp;
2678 tm = localtime (&tt);
2680 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2682 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2684 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2687 if (key->subkeys && (key->subkeys->expires > 0)) {
2688 tt = key->subkeys->expires;
2690 tm = localtime (&tt);
2692 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2694 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2696 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2700 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2704 s2 = is_pgp ? "PGP" : "X.509";
2707 aval = key->subkeys->length;
2709 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2711 fprintf (fp, _("Key Usage .: "));
2714 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2715 fprintf (fp, "%s%s", delim, _("encryption"));
2718 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2719 fprintf (fp, "%s%s", delim, _("signing"));
2722 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2723 fprintf (fp, "%s%s", delim, _("certification"));
2729 s = key->subkeys->fpr;
2730 fputs (_("Fingerprint: "), fp);
2731 if (is_pgp && m_strlen(s) == 40) {
2732 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2737 putc (is_pgp ? ' ' : ':', fp);
2738 if (is_pgp && i == 4)
2743 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2746 putc (is_pgp ? ' ' : ':', fp);
2747 if (is_pgp && i == 7)
2751 fprintf (fp, "%s\n", s);
2754 if (key->issuer_serial) {
2755 s = key->issuer_serial;
2757 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2760 if (key->issuer_name) {
2761 s = key->issuer_name;
2763 fprintf (fp, _("Issued By .: "));
2764 parse_and_print_user_id (fp, s);
2769 /* For PGP we list all subkeys. */
2771 gpgme_subkey_t subkey = NULL;
2773 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2777 if (m_strlen(s) == 16)
2778 s += 8; /* display only the short keyID */
2779 fprintf (fp, _("Subkey ....: 0x%s"), s);
2780 if (subkey->revoked) {
2782 fputs (_("[Revoked]"), fp);
2784 if (subkey->invalid) {
2786 fputs (_("[Invalid]"), fp);
2788 if (subkey->expired) {
2790 fputs (_("[Expired]"), fp);
2792 if (subkey->disabled) {
2794 fputs (_("[Disabled]"), fp);
2798 if (subkey->timestamp > 0) {
2799 tt = subkey->timestamp;
2801 tm = localtime (&tt);
2803 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2805 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2807 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2810 if (subkey->expires > 0) {
2811 tt = subkey->expires;
2813 tm = localtime (&tt);
2815 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2817 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2819 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2823 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2828 aval = subkey->length;
2832 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2834 fprintf (fp, _("Key Usage .: "));
2837 if (subkey->can_encrypt) {
2838 fprintf (fp, "%s%s", delim, _("encryption"));
2841 if (subkey->can_sign) {
2842 fprintf (fp, "%s%s", delim, _("signing"));
2845 if (subkey->can_certify) {
2846 fprintf (fp, "%s%s", delim, _("certification"));
2854 setlocale (LC_TIME, "C");
2858 /* Show detailed information about the selected key */
2859 static void verify_key (cryptkey_t * key)
2862 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2864 gpgme_ctx_t listctx = NULL;
2866 gpgme_key_t k = NULL;
2869 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2871 mutt_perror (_("Can't create temporary file"));
2874 mutt_message _("Collecting data...");
2876 print_key_info (key->kobj, fp);
2878 err = gpgme_new (&listctx);
2880 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2881 gpgme_strerror (err));
2884 if ((key->flags & KEYFLAG_ISX509))
2885 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2889 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2891 err = gpgme_op_keylist_start (listctx, s, 0);
2895 err = gpgme_op_keylist_next (listctx, &k);
2897 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2900 gpgme_op_keylist_end (listctx);
2902 print_key_info (k, fp);
2905 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2912 gpgme_release (listctx);
2914 mutt_clear_error ();
2915 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2916 mutt_pager(cmd, tempfile, 0, NULL);
2919 /* Implementation of `findkeys'. */
2921 static void add_hints(string_array *arr, const char *s)
2927 int l = strcspn(s, " ,.:\"()<>\n");
2928 string_array_append(arr, p_dupstr(s, l));
2930 s += strspn(s, " ,.:\"()<>\n");
2934 /* Return a list of keys which are candidates for the selection. */
2936 get_candidates(string_array *hints, unsigned int app, int secret)
2938 cryptkey_t *res = NULL, **kend = &res;
2943 if (hints->len <= 0)
2945 string_array_append(hints, NULL);
2946 ctx = create_gpgme_context(0);
2948 if ((app & APPLICATION_PGP)) {
2949 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2952 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2953 gpgme_strerror(err));
2958 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2959 gpgme_user_id_t uid = NULL;
2960 unsigned int flags = 0;
2963 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2964 flags |= KEYFLAG_CANENCRYPT;
2965 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2966 flags |= KEYFLAG_CANSIGN;
2968 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2969 cryptkey_t *k = p_new(cryptkey_t, 1);
2978 if (gpg_err_code(err) != GPG_ERR_EOF)
2979 mutt_error(_("gpgme_op_keylist_next failed: %s"), gpgme_strerror(err));
2980 gpgme_op_keylist_end(ctx);
2983 if ((app & APPLICATION_SMIME)) {
2984 gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
2985 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2988 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2989 gpgme_strerror(err));
2994 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2995 gpgme_user_id_t uid = NULL;
2996 unsigned int flags = KEYFLAG_ISX509;
2999 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
3000 flags |= KEYFLAG_CANENCRYPT;
3001 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
3002 flags |= KEYFLAG_CANSIGN;
3004 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3005 cryptkey_t *k = p_new(cryptkey_t, 1);
3014 if (gpg_err_code(err) != GPG_ERR_EOF)
3015 mutt_error(_("gpgme_op_keylist_next failed: %s"),
3016 gpgme_strerror(err));
3017 gpgme_op_keylist_end(ctx);
3024 /* Display a menu to select a key from the array KEYS. FORCED_VALID
3025 will be set to true on return if the user did override the the
3027 static cryptkey_t *crypt_select_key (cryptkey_t * keys,
3028 address_t * p, const char *s,
3029 unsigned int app, int *forced_valid)
3032 cryptkey_t **cryptkey_table;
3035 char buf[LONG_STRING];
3037 int (*f) (const void *, const void *);
3038 int menu_to_use = 0;
3043 /* build the key table */
3045 cryptkey_table = NULL;
3046 for (k = keys; k; k = k->next) {
3047 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
3054 p_realloc(&cryptkey_table, keymax);
3057 cryptkey_table[i++] = k;
3060 if (!i && unusable) {
3061 mutt_error _("All matching keys are marked expired/revoked.");
3067 switch (PgpSortKeys & SORT_MASK) {
3069 f = crypt_compare_date;
3072 f = crypt_compare_keyid;
3075 f = crypt_compare_address;
3079 f = crypt_compare_trust;
3082 qsort (cryptkey_table, i, sizeof (cryptkey_t *), f);
3084 if (app & APPLICATION_PGP)
3085 menu_to_use = MENU_KEY_SELECT_PGP;
3086 else if (app & APPLICATION_SMIME)
3087 menu_to_use = MENU_KEY_SELECT_SMIME;
3089 menu = mutt_new_menu ();
3091 menu->make_entry = crypt_entry;
3092 menu->menu = menu_to_use;
3093 menu->data = cryptkey_table;
3098 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3099 ts = _("PGP and S/MIME keys matching");
3100 else if ((app & APPLICATION_PGP))
3101 ts = _("PGP keys matching");
3102 else if ((app & APPLICATION_SMIME))
3103 ts = _("S/MIME keys matching");
3105 ts = _("keys matching");
3108 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3110 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3114 mutt_clear_error ();
3118 switch (mutt_menuLoop (menu)) {
3120 verify_key (cryptkey_table[menu->current]);
3121 menu->redraw = REDRAW_FULL;
3125 mutt_message ("%s", cryptkey_table[menu->current]->uid);
3128 case OP_GENERIC_SELECT_ENTRY:
3129 /* FIXME make error reporting more verbose - this should be
3130 easy because gpgme provides more information */
3131 if (option (OPTPGPCHECKTRUST)) {
3132 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE ) {
3133 mutt_error(_("This key can't be used: "
3134 "expired/disabled/revoked."));
3139 if (option (OPTPGPCHECKTRUST) &&
3140 ((cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3141 || !crypt_id_is_strong (cryptkey_table[menu->current]))) {
3143 char buff[LONG_STRING];
3145 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3146 s = N_("ID is expired/disabled/revoked.");
3148 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3149 gpgme_user_id_t uid = NULL;
3154 uid = cryptkey_table[menu->current]->kobj->uids;
3155 for (j = 0; (j < cryptkey_table[menu->current]->idx) && uid;
3156 j++, uid = uid->next);
3158 val = uid->validity;
3161 case GPGME_VALIDITY_UNKNOWN:
3162 case GPGME_VALIDITY_UNDEFINED:
3163 warn_s = N_("ID has undefined validity.");
3165 case GPGME_VALIDITY_NEVER:
3166 warn_s = N_("ID is not valid.");
3168 case GPGME_VALIDITY_MARGINAL:
3169 warn_s = N_("ID is only marginally valid.");
3171 case GPGME_VALIDITY_FULL:
3172 case GPGME_VALIDITY_ULTIMATE:
3176 snprintf (buff, sizeof (buff),
3177 _("%s Do you really want to use the key?"), _(warn_s));
3179 if (mutt_yesorno (buff, 0) != 1) {
3180 mutt_clear_error ();
3187 k = cryptkey_dup(cryptkey_table[menu->current]);
3198 mutt_menuDestroy (&menu);
3199 p_delete(&cryptkey_table);
3201 set_option (OPTNEEDREDRAW);
3207 crypt_getkeybyaddr(address_t * a, int abilities, int app, int *forced_valid)
3214 int this_key_has_strong;
3215 int this_key_has_weak;
3216 int this_key_has_invalid;
3219 cryptkey_t *keys, *k;
3220 cryptkey_t *the_valid_key = NULL;
3221 cryptkey_t *matches = NULL;
3222 cryptkey_t **matches_endp = &matches;
3228 string_array_init(&hints);
3229 add_hints(&hints, a->mailbox);
3230 add_hints(&hints, a->personal);
3232 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3233 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3234 string_array_wipe(&hints);
3240 for (k = keys; k; k = k->next) {
3241 if (abilities && !(k->flags & abilities)) {
3245 this_key_has_weak = 0; /* weak but valid match */
3246 this_key_has_invalid = 0; /* invalid match */
3247 this_key_has_strong = 0; /* strong and valid match */
3248 match = 0; /* any match */
3250 r = rfc822_parse_adrlist (NULL, k->uid);
3251 for (p = r; p; p = p->next) {
3252 int validity = crypt_id_matches_addr (a, p, k);
3254 if (validity & CRYPT_KV_MATCH) /* something matches */
3257 /* is this key a strong candidate? */
3258 if ((validity & CRYPT_KV_VALID)
3259 && (validity & CRYPT_KV_STRONGID)
3260 && (validity & CRYPT_KV_ADDR)) {
3261 if (the_valid_key && the_valid_key != k)
3264 this_key_has_strong = 1;
3266 else if ((validity & CRYPT_KV_MATCH)
3267 && !(validity & CRYPT_KV_VALID))
3268 this_key_has_invalid = 1;
3269 else if ((validity & CRYPT_KV_MATCH)
3270 && (!(validity & CRYPT_KV_STRONGID)
3271 || !(validity & CRYPT_KV_ADDR)))
3272 this_key_has_weak = 1;
3274 address_list_wipe(&r);
3279 if (!this_key_has_strong && this_key_has_invalid)
3281 if (!this_key_has_strong && this_key_has_weak)
3284 *matches_endp = tmp = cryptkey_dup(k);
3285 matches_endp = &tmp->next;
3286 the_valid_key = tmp;
3289 key_list_wipe(&keys);
3292 if (the_valid_key && !multi && !weak
3293 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3295 * There was precisely one strong match on a valid ID, there
3296 * were no valid keys with weak matches, and we aren't
3297 * interested in seeing invalid keys.
3299 * Proceed without asking the user.
3301 k = cryptkey_dup(the_valid_key);
3304 * Else: Ask the user.
3306 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3308 key_list_wipe(&matches);
3318 crypt_getkeybystr(const char *p, int abilities, int app, int *forced_valid)
3321 cryptkey_t *matches = NULL;
3322 cryptkey_t **matches_endp = &matches;
3326 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3332 string_array_init(&hints);
3333 add_hints(&hints, p);
3334 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3335 string_array_wipe(&hints);
3341 for (k = keys; k; k = k->next) {
3342 const char *s = crypt_keyid(k);
3344 if (abilities && !(k->flags & abilities))
3349 if (!*p || !m_strcasecmp(p, s)
3350 || (!m_strncasecmp(p, "0x", 2) && !m_strcasecmp(p + 2, s))
3351 || m_stristr(k->uid, p))
3355 *matches_endp = tmp = cryptkey_dup(k);
3356 matches_endp = &tmp->next;
3359 key_list_wipe(&keys);
3362 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3363 key_list_wipe(&matches);
3370 /* Display TAG as a prompt to ask for a key.
3371 * ABILITIES describe the required key abilities (sign, encrypt) and APP the
3372 * type of the requested key; ether S/MIME or PGP.
3373 * Return a copy of the key or NULL if not found. */
3375 crypt_ask_for_key(const char *tag, int abilities, int app, int *forced_valid)
3382 forced_valid = &dummy;
3388 if (mutt_get_field(tag, resp, sizeof(resp), M_CLEAR) != 0)
3391 if (m_strisempty(resp))
3394 if ((key = crypt_getkeybystr(resp, abilities, app, forced_valid)))
3401 static char *crypt_hook(address_t *adr)
3404 lua_State *L = luaM_getruntime();
3405 lua_getglobal(L, "mod_core"); /* push mod_core 1 */
3406 lua_getfield(L, -1, "crypt_hook"); /* push folder_hook() 2 */
3407 if (lua_isfunction(L, -1)) {
3408 lua_pushstring(L, adr->mailbox);
3409 if (!lua_pcall(L, 1, 1, 0)) {
3410 res = m_strdup(lua_tostring(L, -1));
3418 /* This routine attempts to find the keyids of the recipients of a
3419 message. It returns NULL if any of the keys can not be found. */
3420 static char *find_keys(ENVELOPE *env, unsigned int app)
3422 address_t *lst = NULL, *addr;
3423 buffer_t *keylist = buffer_new();
3426 address_t **last = &lst;
3427 *last = address_list_dup(env->to);
3428 last = address_list_last(last);
3429 *last = address_list_dup(env->cc);
3430 last = address_list_last(last);
3431 *last = address_list_dup(env->bcc);
3433 rfc822_qualify(lst, mutt_fqdn(1));
3434 address_list_uniq(lst);
3437 while ((addr = address_list_pop(&lst))) {
3439 int forced_valid = 0;
3441 cryptkey_t *key = NULL;
3443 if ((keyID = crypt_hook(addr))) {
3446 snprintf(buf, sizeof(buf), _("Use keyID = \"%s\" for %s?"), keyID,
3448 r = mutt_yesorno(buf, M_YES);
3451 address_list_wipe(&lst);
3452 address_list_wipe(&addr);
3453 buffer_delete(&keylist);
3460 /* check for e-mail address */
3461 if (strchr(keyID, '@') && (a = rfc822_parse_adrlist(NULL, keyID))) {
3462 rfc822_qualify(a, mutt_fqdn(1));
3463 address_list_wipe(&addr);
3466 key = crypt_getkeybystr(keyID, KEYFLAG_CANENCRYPT, app,
3474 key = crypt_getkeybyaddr(addr, KEYFLAG_CANENCRYPT, app, &forced_valid);
3477 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), addr->mailbox);
3478 key = crypt_ask_for_key(buf, KEYFLAG_CANENCRYPT, app,
3481 address_list_wipe(&lst);
3482 address_list_wipe(&addr);
3483 buffer_delete(&keylist);
3489 buffer_addch(keylist, ' ');
3490 buffer_addstr(keylist, "0x");
3491 buffer_addstr(keylist, crypt_fpr(key));
3493 buffer_addch(keylist, '!');
3495 key_list_wipe(&key);
3496 address_list_wipe(&addr);
3499 address_list_wipe(&lst);
3500 return buffer_unwrap(&keylist);
3503 int crypt_get_keys(HEADER *msg, char **keylist)
3505 /* Do a quick check to make sure that we can find all of the encryption
3506 * keys if the user has requested this service.
3511 if (msg->security & ENCRYPT) {
3512 if (msg->security & APPLICATION_PGP) {
3513 set_option(OPTPGPCHECKTRUST);
3514 *keylist = find_keys(msg->env, APPLICATION_PGP);
3515 unset_option(OPTPGPCHECKTRUST);
3520 if (msg->security & APPLICATION_SMIME) {
3521 *keylist = find_keys(msg->env, APPLICATION_SMIME);
3531 int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
3537 if (msg->security & APPLICATION_SMIME)
3539 if (msg->security & APPLICATION_PGP)
3543 ? mutt_multi_choice(_("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3545 : mutt_multi_choice(_("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3549 case 1: /* (e)ncrypt */
3550 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3551 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3554 case 2: /* (s)ign */
3555 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3556 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3559 case 3: /* sign (a)s */
3560 p = crypt_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN,
3561 is_smime ? APPLICATION_SMIME : APPLICATION_PGP,
3564 snprintf(buf, sizeof(buf), "0x%s", crypt_keyid(p));
3565 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs, buf);
3567 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3569 *redraw = REDRAW_FULL;
3572 case 4: /* (b)oth */
3574 msg->security = SMIMEENCRYPT | SMIMESIGN;
3576 msg->security = PGPENCRYPT | PGPSIGN;
3580 case 5: /* (p)gp or s/(m)ime */
3581 is_smime = !is_smime;
3584 case 6: /* (c)lear */
3585 return msg->security = 0;
3589 msg->security &= ~APPLICATION_PGP;
3590 msg->security |= APPLICATION_SMIME;
3592 msg->security &= ~APPLICATION_SMIME;
3593 msg->security |= APPLICATION_PGP;
3596 return msg->security;
3599 int crypt_smime_verify_sender(HEADER *h)
3601 address_t *sender = NULL;
3602 unsigned int ret = 1;
3605 h->env->from = mutt_expand_aliases(h->env->from);
3606 sender = h->env->from;
3607 } else if (h->env->sender) {
3608 h->env->sender = mutt_expand_aliases (h->env->sender);
3609 sender = h->env->sender;
3613 mutt_any_key_to_continue ("Failed to figure out sender");
3617 if (signature_key) {
3618 gpgme_key_t key = signature_key;
3619 gpgme_user_id_t uid = NULL;
3620 int sender_length = 0;
3623 sender_length = m_strlen(sender->mailbox);
3624 for (uid = key->uids; uid && ret; uid = uid->next) {
3625 uid_length = m_strlen(uid->email);
3626 if (1 && (uid->email[0] == '<')
3627 && (uid->email[uid_length - 1] == '>')
3628 && (uid_length == sender_length + 2)
3629 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3633 mutt_any_key_to_continue ("Failed to verify sender");
3637 if (signature_key) {
3638 gpgme_key_unref(signature_key);
3639 signature_key = NULL;
3644 static void crypt_invoke_import(FILE *stream, int smime)
3646 gpgme_ctx_t ctx = create_gpgme_context(smime);
3650 err = gpgme_data_new_from_stream(&data, stream);
3652 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3657 err = gpgme_op_import(ctx, data);
3659 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3660 gpgme_data_release(data);
3665 gpgme_data_release(data);
3670 static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
3673 FILE *tmpfp = tmpfile();
3675 if (tmpfp == NULL) {
3676 mutt_perror (_("Can't create temporary file"));
3683 mutt_body_handler(top, &s);
3686 crypt_invoke_import(tmpfp, 0);
3690 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3694 for (; top; top = top->next) {
3695 if (!tag || top->tagged)
3696 pgp_extract_keys_from_attachment (fp, top);
3703 int mutt_protect (HEADER * msg, char *keylist)
3705 BODY *pbody = NULL, *tmp_pbody = NULL;
3706 BODY *tmp_smime_pbody = NULL;
3707 BODY *tmp_pgp_pbody = NULL;
3708 int flags = msg->security;
3713 tmp_smime_pbody = msg->content;
3714 tmp_pgp_pbody = msg->content;
3716 if (msg->security & SIGN) {
3717 if (msg->security & APPLICATION_SMIME) {
3718 if (!(tmp_pbody = sign_message(msg->content, 1)))
3720 pbody = tmp_smime_pbody = tmp_pbody;
3723 if ((msg->security & APPLICATION_PGP)
3724 && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
3725 if (!(tmp_pbody = sign_message(msg->content, 0)))
3729 pbody = tmp_pgp_pbody = tmp_pbody;
3732 if ((msg->security & APPLICATION_SMIME)
3733 && (msg->security & APPLICATION_PGP)) {
3734 /* here comes the draft ;-) */
3739 if (msg->security & ENCRYPT) {
3740 if ((msg->security & APPLICATION_SMIME)) {
3741 if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
3743 /* signed ? free it! */
3746 /* free tmp_body if messages was signed AND encrypted ... */
3747 if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
3748 /* detatch and dont't delete msg->content,
3749 which tmp_smime_pbody->parts after signing. */
3750 tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
3751 msg->content->next = NULL;
3752 body_list_wipe(&tmp_smime_pbody);
3757 if ((msg->security & APPLICATION_PGP)) {
3758 if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
3761 /* did we perform a retainable signature? */
3762 if (flags != msg->security) {
3763 /* remove the outer multipart layer */
3764 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3765 /* get rid of the signature */
3766 body_list_wipe(&tmp_pgp_pbody->next);
3772 /* destroy temporary signature envelope when doing retainable
3776 if (flags != msg->security) {
3777 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3778 body_list_wipe(&tmp_pgp_pbody->next);
3784 msg->content = pbody;
3790 int crypt_query (BODY * m)
3797 if (m->type == TYPEAPPLICATION) {
3798 t |= mutt_is_application_pgp (m);
3800 t |= mutt_is_application_smime (m);
3801 if (t && m->goodsig)
3806 else if (m->type == TYPETEXT) {
3807 t |= mutt_is_application_pgp (m);
3808 if (t && m->goodsig)
3812 if (m->type == TYPEMULTIPART) {
3813 t |= mutt_is_multipart_encrypted (m);
3814 t |= mutt_is_multipart_signed (m);
3816 if (t && m->goodsig)
3820 if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
3824 u = m->parts ? ~0 : 0; /* Bits set in all parts */
3825 w = 0; /* Bits set in any part */
3827 for (p = m->parts; p; p = p->next) {
3828 v = crypt_query (p);
3832 t |= u | (w & ~GOODSIGN);
3834 if ((w & GOODSIGN) && !(u & GOODSIGN))
3842 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
3848 fseeko (s->fpin, a->hdr_offset, 0);
3849 bytes = a->length + a->offset - a->hdr_offset;
3852 if ((c = fgetc (s->fpin)) == EOF)
3860 if (c == '\n' && !hadcr)
3869 static void extract_keys_aux(FILE *fpout, HEADER *h)
3871 mutt_parse_mime_message (Context, h);
3874 if (h->security & APPLICATION_PGP) {
3875 mutt_copy_message(fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
3878 mutt_endwin (_("Trying to extract PGP keys...\n"));
3881 if (h->security & APPLICATION_SMIME) {
3882 if (h->security & ENCRYPT)
3883 mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
3884 | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
3886 mutt_copy_message(fpout, Context, h, 0, 0);
3889 mutt_message (_("Trying to extract S/MIME certificates...\n"));
3893 crypt_invoke_import(fpout, h->security & APPLICATION_SMIME);
3896 void crypt_extract_keys_from_messages(HEADER * h)
3898 FILE *tmpfp = tmpfile();
3900 mutt_error(_("Could not create temporary file"));
3906 for (i = 0; i < Context->vcount; i++) {
3907 if (!Context->hdrs[Context->v2r[i]]->tagged)
3909 extract_keys_aux(tmpfp, Context->hdrs[Context->v2r[i]]);
3912 extract_keys_aux(tmpfp, h);
3917 mutt_any_key_to_continue(NULL);
3920 static void crypt_fetch_signatures(BODY ***signatures, BODY * a, int *n)
3922 for (; a; a = a->next) {
3923 if (a->type == TYPEMULTIPART) {
3924 crypt_fetch_signatures(signatures, a->parts, n);
3927 p_realloc(signatures, *n + 6);
3929 (*signatures)[(*n)++] = a;
3934 int mutt_signed_handler(BODY *a, STATE *s)
3936 unsigned major, minor;
3938 int rc, i, goodsig = 1, sigcnt = 0;
3941 protocol = parameter_getval(a->parameter, "protocol");
3944 switch (mime_which_token(protocol, -1)) {
3945 case MIME_APPLICATION_PGP_SIGNATURE:
3946 major = TYPEAPPLICATION;
3947 minor = MIME_PGP_SIGNATURE;
3949 case MIME_APPLICATION_X_PKCS7_SIGNATURE:
3950 major = TYPEAPPLICATION;
3951 minor = MIME_X_PKCS7_SIGNATURE;
3953 case MIME_APPLICATION_PKCS7_SIGNATURE:
3954 major = TYPEAPPLICATION;
3955 minor = MIME_PKCS7_SIGNATURE;
3957 case MIME_MULTIPART_MIXED:
3958 major = TYPEMULTIPART;
3963 state_printf(s, _("[-- Error: "
3964 "Unknown multipart/signed protocol %s! --]\n\n"),
3966 return mutt_body_handler (a, s);
3969 /* consistency check */
3970 if (!(a && a->next && a->next->type == major &&
3971 mime_which_token(a->next->subtype, -1) == minor))
3973 state_attach_puts(_("[-- Error: "
3974 "Inconsistent multipart/signed structure! --]\n\n"),
3976 return mutt_body_handler (a, s);
3979 if (s->flags & M_DISPLAY) {
3982 crypt_fetch_signatures (&sigs, a->next, &sigcnt);
3984 FILE *tmpfp = tmpfile();
3987 mutt_error(_("Could not create temporary file"));
3989 crypt_write_signed(a, s, tmpfp);
3991 for (i = 0; i < sigcnt; i++) {
3992 if (sigs[i]->type == TYPEAPPLICATION) {
3995 switch ((subtype = mime_which_token(sigs[i]->subtype, -1))) {
3996 case MIME_PGP_SIGNATURE:
3997 case MIME_X_PKCS7_SIGNATURE:
3998 case MIME_PKCS7_SIGNATURE:
3999 if (crypt_verify_one(sigs[i], s, tmpfp, subtype != MIME_PGP_SIGNATURE) != 0)
4010 state_printf(s, _("[-- Warning: "
4011 "We can't verify %s/%s signatures. --]\n\n"),
4012 TYPE (sigs[i]), sigs[i]->subtype);
4016 b->goodsig = goodsig;
4017 b->badsig = !goodsig;
4019 /* Now display the signed body */
4020 state_attach_puts(_("[-- The following data is signed --]\n\n"), s);
4024 state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"),
4029 rc = mutt_body_handler (a, s);
4031 if (s->flags & M_DISPLAY && sigcnt)
4032 state_attach_puts (_("\n[-- End of signed data --]\n"), s);
4037 static int _mutt_check_traditional_pgp (HEADER * h, int *redraw)
4042 h->security |= PGP_TRADITIONAL_CHECKED;
4044 mutt_parse_mime_message (Context, h);
4045 if ((msg = mx_open_message (Context, h->msgno)) == NULL)
4047 if (crypt_pgp_check_traditional (msg->fp, h->content, 0)) {
4048 h->security = crypt_query (h->content);
4049 *redraw |= REDRAW_FULL;
4053 h->security |= PGP_TRADITIONAL_CHECKED;
4054 mx_close_message (&msg);
4058 int mutt_check_traditional_pgp (HEADER * h, int *redraw)
4063 if (h && !(h->security & PGP_TRADITIONAL_CHECKED))
4064 rv = _mutt_check_traditional_pgp (h, redraw);
4066 for (i = 0; i < Context->vcount; i++)
4067 if (Context->hdrs[Context->v2r[i]]->tagged &&
4068 !(Context->hdrs[Context->v2r[i]]->
4069 security & PGP_TRADITIONAL_CHECKED))
4071 _mutt_check_traditional_pgp (Context->hdrs[Context->v2r[i]], redraw)