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 @import "lib-lua/base.cpkg"
38 ** This variable controls whether or not Madmutt may automatically enable
39 ** S/MIME encryption/signing for messages. See also ``$$crypt_autoencrypt'',
40 ** ``$$crypt_replyencrypt'',
41 ** ``$$crypt_autosign'', ``$$crypt_replysign'' and ``$$smime_is_default''.
46 ** This variable controls whether or not Madmutt may automatically enable
47 ** PGP encryption/signing for messages. See also ``$$crypt_autoencrypt'',
48 ** ``$$crypt_replyencrypt'',
49 ** ``$$crypt_autosign'', ``$$crypt_replysign'' and ``$$smime_is_default''.
54 ** Setting this variable will cause Madmutt to always attempt to
55 ** cryptographically sign outgoing messages. This can be overridden
56 ** by use of the \fIpgp-menu\fP, when signing is not required or
57 ** encryption is requested as well. If ``$$smime_is_default'' is \fIset\fP,
58 ** then OpenSSL is used instead to create S/MIME messages and settings can
59 ** be overridden by use of the \fIsmime-menu\fP.
65 ** Setting this variable will cause Madmutt to always attempt to PGP
66 ** encrypt outgoing messages. This is probably only useful in
67 ** connection to the \fIsend-hook\fP command. It can be overridden
68 ** by use of the \fIpgp-menu\fP, when encryption is not required or
69 ** signing is requested as well. If ``$$smime_is_default'' is \fIset\fP,
70 ** then OpenSSL is used instead to create S/MIME messages and
71 ** settings can be overridden by use of the \fIsmime-menu\fP.
74 bool replyencrypt = 1;
77 ** If \fIset\fP, automatically PGP or OpenSSL encrypt replies to messages which are
84 ** If \fIset\fP, automatically PGP or OpenSSL sign replies to messages which are
87 ** \fBNote:\fP this does not work on messages that are encrypted \fBand\fP signed!
90 bool replysignencrypted = 1;
93 ** If \fIset\fP, automatically PGP or OpenSSL sign replies to messages
94 ** which are encrypted. This makes sense in combination with
95 ** ``$$crypt_replyencrypt'', because it allows you to sign all
96 ** messages which are automatically encrypted. This works around
97 ** the problem noted in ``$$crypt_replysign'', that Madmutt is not able
98 ** to find out whether an encrypted message is also signed.
101 quadopt_t verify_sig = M_YES;
104 ** If ``\fIyes\fP'', always attempt to verify PGP or S/MIME signatures.
105 ** If ``\fIask\fP'', ask whether or not to verify the signature.
106 ** If ``\fIno\fP'', never attempt to verify cryptographic signatures.
111 /* Values used for comparing addresses. */
112 #define CRYPT_KV_VALID 1
113 #define CRYPT_KV_ADDR 2
114 #define CRYPT_KV_STRING 4
115 #define CRYPT_KV_STRONGID 8
116 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
123 /* We work based on user IDs, getting from a user ID to the key is
124 check and does not need any memory (gpgme uses reference counting). */
125 typedef struct cryptkey_t {
126 struct cryptkey_t *next;
127 int idx; /* and the user ID at this index */
128 int flags; /* global and per uid flags (for convenience) */
130 const char *uid; /* and for convenience point to this user ID */
133 DO_INIT(cryptkey_t, cryptkey);
134 static void cryptkey_wipe(cryptkey_t *key) {
135 gpgme_key_release(key->kobj);
137 DO_NEW(cryptkey_t, cryptkey);
138 DO_DELETE(cryptkey_t, cryptkey);
139 DO_SLIST(cryptkey_t, key, cryptkey_delete);
141 static cryptkey_t *cryptkey_dup(const cryptkey_t *k)
143 cryptkey_t *res = cryptkey_new();
146 gpgme_key_ref(k->kobj);
150 typedef struct crypt_entry {
155 static gpgme_key_t signature_key = NULL;
157 static void convert_to_7bit (BODY * a)
159 for (; a; a = a->next) {
160 int tok = mime_which_token(a->subtype, -1);
162 if (a->type == TYPEMULTIPART) {
163 a->encoding = ENC7BIT;
164 convert_to_7bit(a->parts);
165 } else if (a->type == TYPEMESSAGE && tok == MIME_DELIVERY_STATUS) {
166 if (a->encoding != ENC7BIT)
167 mutt_message_to_7bit(a, NULL);
168 } else if (a->encoding == ENC8BIT) {
169 a->encoding = ENCQUOTEDPRINTABLE;
170 } else if (a->encoding == ENCBINARY) {
171 a->encoding = ENCBASE64;
172 } else if (a->content && a->encoding != ENCBASE64
173 && (a->content->from || a->content->space))
175 a->encoding = ENCQUOTEDPRINTABLE;
180 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
181 FP. Convert the character set. */
182 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
186 tstr = p_dupstr(buf, len);
187 mutt_convert_string(&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
192 /* Return the keyID for the key K. Note that this string is valid as
193 long as K is valid */
194 static const char *crypt_keyid (cryptkey_t * k)
196 if (k->kobj && k->kobj->subkeys) {
197 const char *s = k->kobj->subkeys->keyid;
198 return m_strlen(s) == 16 ? s + 8 : s;
204 /* Return the hexstring fingerprint from the key K. */
205 static const char *crypt_fpr (cryptkey_t * k)
207 return k->kobj && k->kobj->subkeys ? k->kobj->subkeys->fpr : "";
210 /* Parse FLAGS and return a statically allocated(!) string with them. */
211 static char *crypt_key_abilities (int flags)
213 static char buff[3] = "es";
215 if (!(flags & KEYFLAG_CANENCRYPT))
217 else if (flags & KEYFLAG_PREFER_SIGNING)
220 if (!(flags & KEYFLAG_CANSIGN))
222 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
228 /* Parse FLAGS and return a character describing the most important flag. */
229 static char crypt_flags(int flags)
231 if (flags & KEYFLAG_REVOKED)
233 if (flags & KEYFLAG_EXPIRED)
235 if (flags & KEYFLAG_DISABLED)
237 if (flags & KEYFLAG_CRITICAL)
242 /* Return true whe validity of KEY is sufficient. */
243 static int crypt_id_is_strong (cryptkey_t * key)
248 if (key->flags & KEYFLAG_ISX509)
251 for (i = 0, uid = key->kobj->uids; uid; i++, uid = uid->next) {
253 return uid->validity == GPGME_VALIDITY_FULL
254 || uid->validity == GPGME_VALIDITY_ULTIMATE;
261 /* Return a bit vector describing how well the addresses ADDR and
262 U_ADDR match and whether KEY is valid. */
264 crypt_id_matches_addr(address_t *addr, address_t *u_addr, cryptkey_t *key)
268 if (!(key->flags & KEYFLAG_CANTUSE))
269 rv |= CRYPT_KV_VALID;
271 if (crypt_id_is_strong(key))
272 rv |= CRYPT_KV_STRONGID;
274 if (addr->mailbox && !m_strcasecmp(addr->mailbox, u_addr->mailbox))
277 if (addr->personal && m_strcasecmp(addr->personal, u_addr->personal))
278 rv |= CRYPT_KV_STRING;
284 /* Create a new gpgme context and return it. With FOR_SMIME set to
285 true, the protocol of the context is set to CMS. */
286 static gpgme_ctx_t create_gpgme_context(int for_smime)
291 err = gpgme_new (&ctx);
293 mutt_error(_("error creating gpgme context: %s\n"),
294 gpgme_strerror(err));
301 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
303 mutt_error(_("error enabling CMS protocol: %s\n"), gpgme_strerror(err));
310 /* Create a new gpgme data object. This is a wrapper to die on
312 static gpgme_data_t create_gpgme_data(void)
317 err = gpgme_data_new(&data);
319 mutt_error(_("error creating gpgme data object: %s\n"),
320 gpgme_strerror(err));
327 /* Create a new GPGME Data object from the mail body A. With CONVERT
328 passed as true, the lines are converted to CR,LF if required.
329 Return NULL on error or the gpgme_data_t object on success. */
330 static gpgme_data_t body_to_data_object(BODY *a, int convert)
336 if (!(fptmp = tmpfile())) {
337 mutt_perror (_("Can't create temporary file"));
341 mutt_write_mime_header(a, fptmp);
343 mutt_write_mime_body(a, fptmp);
350 data = create_gpgme_data();
352 while (fgets(buf + spare, sizeof(buf) - 1, fptmp)) {
353 int l = m_strlen(buf);
355 spare = buf[l - 1] != '\n';
356 if (!spare && (l <= 1 || buf[l - 2] != '\r')) {
360 gpgme_data_write(data, buf, l - spare);
365 gpgme_data_write(data, buf, 1);
366 gpgme_data_seek(data, 0, SEEK_SET);
371 err = gpgme_data_new_from_stream(&data, fptmp);
374 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
380 /* Create a GPGME data object from the stream FP but limit the object
381 to LENGTH bytes starting at OFFSET bytes from the beginning of the
383 static gpgme_data_t file_to_data_object(FILE *fp, long offset, long length)
388 err = gpgme_data_new_from_filepart(&data, NULL, fp, offset, length);
390 mutt_error(_("error allocating data object: %s\n"),
391 gpgme_strerror(err));
398 /* Write a GPGME data object to the stream FP. */
399 static int data_object_to_stream(gpgme_data_t data, FILE *fp)
405 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
406 ? gpgme_error_from_errno (errno) : 0);
408 mutt_error (_("error rewinding data object: %s\n"),
409 gpgme_strerror(err));
413 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
414 /* fixme: we are not really converting CRLF to LF but just
415 skipping CR. Doing it correctly needs a more complex logic */
416 for (p = buf; nread; p++, nread--) {
422 mutt_perror ("[tempfile]");
427 mutt_error(_("error reading data object: %s\n"), strerror(errno));
433 /* Copy a data object to a newly created temporay file and return that
434 filename. Caller must free. With RET_FP not NULL, don't close the
435 stream but return it there. */
436 static char *data_object_to_tempfile(gpgme_data_t data, FILE **ret_fp)
439 char tempfile[_POSIX_PATH_MAX];
443 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
445 mutt_perror (_("Can't create temporary file"));
449 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
450 ? gpgme_error_from_errno (errno) : 0);
454 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
455 if (fwrite (buf, nread, 1, fp) != 1) {
456 mutt_perror (_("Can't create temporary file"));
465 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
476 return m_strdup(tempfile);
480 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
482 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
488 if (!ctx || !r_key || !fpr)
489 return gpg_error (GPG_ERR_INV_VALUE);
491 if (strlen (fpr) < 8) /* We have at least a key ID. */
492 return gpg_error (GPG_ERR_INV_VALUE);
494 /* FIXME: We use our own context because we have to avoid the user's
495 I/O callback handlers. */
496 err = gpgme_new (&listctx);
499 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
500 err = gpgme_op_keylist_start (listctx, fpr, secret);
502 err = gpgme_op_keylist_next (listctx, r_key);
503 gpgme_release (listctx);
507 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
508 The keys must be space delimited. */
509 static gpgme_key_t *create_recipient_set(const char *s, int smime)
511 gpgme_ctx_t ctx = create_gpgme_context(smime);
512 gpgme_key_t *rset = NULL;
519 const char *p = m_strnextsp(s);
522 m_strncpy(buf, sizeof(buf), s, p - s);
523 if (p - s > 1 && p[-1] == '!') {
524 /* user wants to override the valididy of that key. */
526 buf[p - s - 1] = '\0';
527 err = gpgme_get_key2(ctx, buf, &key, 0);
529 key->uids->validity = GPGME_VALIDITY_FULL;
531 err = gpgme_get_key2(ctx, buf, &key, 0);
535 mutt_error(_("error adding recipient `%.*s': %s\n"),
536 (int)(p - s), s, gpgme_strerror(err));
541 p_realloc(&rset, rset_n + 1);
542 rset[rset_n++] = key;
547 /* NULL terminate. */
548 p_realloc(&rset, rset_n + 1);
549 rset[rset_n++] = NULL;
557 /* Make sure that the correct signer is set. Returns 0 on success. */
558 static int set_signer(gpgme_ctx_t ctx, int for_smime)
560 const char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
564 if (m_strisempty(signid))
567 err = gpgme_get_key(ctx, signid, &key, 1);
569 mutt_error(_("error getting secret key `%s': %s\n"), signid,
570 gpgme_strerror(err));
574 gpgme_signers_clear(ctx);
575 err = gpgme_signers_add(ctx, key);
576 gpgme_key_unref(key);
578 mutt_error(_("error setting secret key `%s': %s\n"), signid,
579 gpgme_strerror(err));
586 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
587 and return an allocated filename to a temporary file containing the
588 enciphered text. With USE_SMIME set to true, the smime backend is
589 used. With COMBINED_SIGNED a PGP message is signed and
590 encrypted. Returns NULL in case of error */
591 static char *encrypt_gpgme_object(gpgme_data_t plaintext, gpgme_key_t *rset,
592 int use_smime, int combined_signed)
596 gpgme_data_t ciphertext;
599 ctx = create_gpgme_context (use_smime);
601 gpgme_set_armor (ctx, 1);
603 ciphertext = create_gpgme_data ();
605 if (combined_signed) {
606 if (set_signer(ctx, use_smime)) {
607 gpgme_data_release(ciphertext);
611 err = gpgme_op_encrypt_sign(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
612 plaintext, ciphertext);
614 err = gpgme_op_encrypt(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
615 plaintext, ciphertext);
618 mutt_need_hard_redraw();
620 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
621 gpgme_data_release (ciphertext);
628 outfile = data_object_to_tempfile(ciphertext, NULL);
629 gpgme_data_release(ciphertext);
633 /* Find the "micalg" parameter from the last Gpgme operation on
634 context CTX. It is expected that this operation was a sign
635 operation. Return the algorithm name as a C string in buffer BUF
636 which must have been allocated by the caller with size BUFLEN.
637 Returns 0 on success or -1 in case of an error. The return string
638 is truncted to BUFLEN - 1. */
639 static int get_micalg(gpgme_ctx_t ctx, char *buf, ssize_t buflen)
641 gpgme_sign_result_t result = NULL;
642 const char *alg = NULL;
644 result = gpgme_op_sign_result(ctx);
645 if (result && result->signatures) {
646 alg = gpgme_hash_algo_name(result->signatures->hash_algo);
648 m_strcpy(buf, buflen, NONULL(alg));
653 static void print_time(time_t t, STATE *s)
657 setlocale(LC_TIME, "");
658 #ifdef HAVE_LANGINFO_D_T_FMT
659 strftime(p, sizeof(p), nl_langinfo(D_T_FMT), localtime(&t));
661 strftime(p, sizeof(p), "%c", localtime(&t));
663 setlocale(LC_TIME, "C");
664 state_attach_puts(p, s);
667 /* Implementation of `sign_message'. */
669 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
670 USE_SMIME is passed as true. Returns the new body or NULL on
672 static BODY *sign_message(BODY * a, int use_smime)
679 gpgme_data_t message, signature;
681 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
683 message = body_to_data_object(a, 1);
686 signature = create_gpgme_data ();
688 ctx = create_gpgme_context (use_smime);
690 gpgme_set_armor (ctx, 1);
692 if (set_signer (ctx, use_smime)) {
693 gpgme_data_release (signature);
698 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
699 mutt_need_hard_redraw ();
700 gpgme_data_release (message);
702 gpgme_data_release (signature);
704 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
708 sigfile = data_object_to_tempfile(signature, NULL);
709 gpgme_data_release (signature);
716 t->type = TYPEMULTIPART;
717 t->subtype = m_strdup("signed");
718 t->encoding = ENC7BIT;
720 t->disposition = DISPINLINE;
722 parameter_set_boundary(&t->parameter);
723 parameter_setval(&t->parameter, "protocol",
724 use_smime ? "application/pkcs7-signature"
725 : "application/pgp-signature");
726 /* Get the micalg from gpgme. Old gpgme versions don't support this
727 for S/MIME so we assume sha-1 in this case. */
728 if (!get_micalg (ctx, buf, sizeof buf))
729 parameter_setval(&t->parameter, "micalg", buf);
731 parameter_setval(&t->parameter, "micalg", "sha1");
737 t->parts->next = body_new();
739 t->type = TYPEAPPLICATION;
741 t->subtype = m_strdup("pkcs7-signature");
742 parameter_setval(&t->parameter, "name", "smime.p7s");
743 t->encoding = ENCBASE64;
745 t->disposition = DISPATTACH;
746 t->d_filename = m_strdup("smime.p7s");
749 t->subtype = m_strdup("pgp-signature");
751 t->disposition = DISPINLINE;
752 t->encoding = ENC7BIT;
754 t->filename = sigfile;
755 t->unlink = 1; /* ok to remove this file after sending. */
760 /* Encrypt the mail body A to all keys given as space separated keyids
761 or fingerprints in KEYLIST and return the encrypted body. */
762 static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
764 char *outfile = NULL;
766 gpgme_key_t *rset = NULL;
767 gpgme_data_t plaintext;
769 rset = create_recipient_set(keylist, 0);
775 plaintext = body_to_data_object(a, 0);
781 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
782 gpgme_data_release (plaintext);
788 t->type = TYPEMULTIPART;
789 t->subtype = m_strdup("encrypted");
790 t->encoding = ENC7BIT;
792 t->disposition = DISPINLINE;
794 parameter_set_boundary(&t->parameter);
795 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
797 t->parts = body_new();
798 t->parts->type = TYPEAPPLICATION;
799 t->parts->subtype = m_strdup("pgp-encrypted");
800 t->parts->encoding = ENC7BIT;
802 t->parts->next = body_new();
803 t->parts->next->type = TYPEAPPLICATION;
804 t->parts->next->subtype = m_strdup("octet-stream");
805 t->parts->next->encoding = ENC7BIT;
806 t->parts->next->filename = outfile;
807 t->parts->next->use_disp = 1;
808 t->parts->next->disposition = DISPINLINE;
809 t->parts->next->unlink = 1; /* delete after sending the message */
810 t->parts->next->d_filename = m_strdup("msg.asc");
815 /* Encrypt the mail body A to all keys given as space separated
816 fingerprints in KEYLIST and return the S/MIME encrypted body. */
817 static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
819 char *outfile = NULL;
821 gpgme_key_t *rset = NULL;
822 gpgme_data_t plaintext;
824 rset = create_recipient_set(keylist, 1);
828 plaintext = body_to_data_object(a, 0);
834 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
835 gpgme_data_release (plaintext);
841 t->type = TYPEAPPLICATION;
842 t->subtype = m_strdup("pkcs7-mime");
843 parameter_setval(&t->parameter, "name", "smime.p7m");
844 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
845 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
847 t->disposition = DISPATTACH;
848 t->d_filename = m_strdup("smime.p7m");
849 t->filename = outfile;
850 t->unlink = 1; /*delete after sending the message */
857 /* Display the common attributes of the signature summary SUM.
858 Return 1 if there is is a severe warning.
860 static int show_sig_summary (unsigned long sum,
861 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
866 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
867 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
871 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
872 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
875 state_attach_puts (_("Warning: The key used to create the "
876 "signature expired at: "), s);
878 state_attach_puts ("\n", s);
881 state_attach_puts (_("Warning: At least one certification key "
882 "has expired\n"), s);
885 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
886 gpgme_verify_result_t result;
887 gpgme_signature_t sig;
890 result = gpgme_op_verify_result (ctx);
892 for (sig = result->signatures, i = 0; sig && (i < idx);
893 sig = sig->next, i++);
895 state_attach_puts (_("Warning: The signature expired at: "), s);
896 print_time (sig ? sig->exp_timestamp : 0, s);
897 state_attach_puts ("\n", s);
900 if ((sum & GPGME_SIGSUM_KEY_MISSING))
901 state_attach_puts (_("Can't verify due to a missing "
902 "key or certificate\n"), s);
904 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
905 state_attach_puts (_("The CRL is not available\n"), s);
909 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
910 state_attach_puts (_("Available CRL is too old\n"), s);
914 if ((sum & GPGME_SIGSUM_BAD_POLICY))
915 state_attach_puts (_("A policy requirement was not met\n"), s);
917 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
918 const char *t0 = NULL, *t1 = NULL;
919 gpgme_verify_result_t result;
920 gpgme_signature_t sig;
923 state_attach_puts (_("A system error occurred"), s);
925 /* Try to figure out some more detailed system error information. */
926 result = gpgme_op_verify_result (ctx);
927 for (sig = result->signatures, i = 0; sig && (i < idx);
928 sig = sig->next, i++);
931 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
935 state_attach_puts (": ", s);
937 state_attach_puts (t0, s);
938 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
940 state_attach_puts (",", s);
941 state_attach_puts (t1, s);
944 state_attach_puts ("\n", s);
951 static void show_fingerprint (gpgme_key_t key, STATE * state)
956 const char *prefix = _("Fingerprint: ");
961 s = key->subkeys ? key->subkeys->fpr : NULL;
964 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
966 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
967 buf = p_new(char, bufsize);
968 m_strcpy(buf, bufsize, prefix);
969 p = buf + m_strlen(buf);
970 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
971 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
982 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
985 *p++ = is_pgp ? ' ' : ':';
986 if (is_pgp && i == 7)
991 /* just in case print remaining odd digits */
996 state_attach_puts (buf, state);
1000 /* Show the valididy of a key used for one signature. */
1001 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
1003 gpgme_verify_result_t result = NULL;
1004 gpgme_signature_t sig = NULL;
1005 const char *txt = NULL;
1007 result = gpgme_op_verify_result (ctx);
1009 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
1011 switch (sig ? sig->validity : 0) {
1012 case GPGME_VALIDITY_UNKNOWN:
1013 txt = _("WARNING: We have NO indication whether "
1014 "the key belongs to the person named " "as shown above\n");
1016 case GPGME_VALIDITY_UNDEFINED:
1018 case GPGME_VALIDITY_NEVER:
1019 txt = _("WARNING: The key does NOT BELONG to "
1020 "the person named as shown above\n");
1022 case GPGME_VALIDITY_MARGINAL:
1023 txt = _("WARNING: It is NOT certain that the key "
1024 "belongs to the person named as shown above\n");
1026 case GPGME_VALIDITY_FULL:
1027 case GPGME_VALIDITY_ULTIMATE:
1032 state_attach_puts (txt, s);
1035 /* Show information about one signature. This fucntion is called with
1036 the context CTX of a sucessful verification operation and the
1037 enumerator IDX which should start at 0 and incremete for each
1040 Return values are: 0 for normal procession, 1 for a bad signature,
1041 2 for a signature with a warning or -1 for no more signature. */
1042 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
1045 const char *fpr, *uid;
1046 gpgme_key_t key = NULL;
1047 int i, anybad = 0, anywarn = 0;
1049 gpgme_user_id_t uids = NULL;
1050 gpgme_verify_result_t result;
1051 gpgme_signature_t sig;
1052 gpgme_error_t err = GPG_ERR_NO_ERROR;
1054 result = gpgme_op_verify_result (ctx);
1056 /* FIXME: this code should use a static variable and remember
1057 the current position in the list of signatures, IMHO.
1060 for (i = 0, sig = result->signatures; sig && (i < idx);
1061 i++, sig = sig->next);
1063 return -1; /* Signature not found. */
1065 if (signature_key) {
1066 gpgme_key_unref(signature_key);
1067 signature_key = NULL;
1070 created = sig->timestamp;
1074 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1077 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1079 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1081 signature_key = key;
1084 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1085 error. Do it here to avoid a double free. */
1089 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1091 state_attach_puts (_("Error getting key information: "), s);
1092 state_attach_puts (gpg_strerror (err), s);
1093 state_attach_puts ("\n", s);
1096 else if ((sum & GPGME_SIGSUM_GREEN)) {
1097 state_attach_puts (_("Good signature from: "), s);
1098 state_attach_puts (uid, s);
1099 state_attach_puts ("\n", s);
1100 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1102 /* Skip primary UID. */
1106 state_attach_puts (_(" aka: "), s);
1107 state_attach_puts (uids->uid, s);
1108 state_attach_puts ("\n", s);
1110 state_attach_puts (_(" created: "), s);
1111 print_time (created, s);
1112 state_attach_puts ("\n", s);
1113 if (show_sig_summary (sum, ctx, key, idx, s))
1115 show_one_sig_validity (ctx, idx, s);
1117 else if ((sum & GPGME_SIGSUM_RED)) {
1118 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1119 state_attach_puts (uid, s);
1120 state_attach_puts ("\n", s);
1121 show_sig_summary (sum, ctx, key, idx, s);
1123 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1124 signature, so we display what a PGP user expects: The name,
1125 fingerprint and the key validity (which is neither fully or
1127 state_attach_puts (_("Good signature from: "), s);
1128 state_attach_puts (uid, s);
1129 state_attach_puts ("\n", s);
1130 state_attach_puts (_(" created: "), s);
1131 print_time (created, s);
1132 state_attach_puts ("\n", s);
1133 show_one_sig_validity (ctx, idx, s);
1134 show_fingerprint (key, s);
1135 if (show_sig_summary (sum, ctx, key, idx, s))
1138 else { /* can't decide (yellow) */
1140 state_attach_puts (_("Error checking signature"), s);
1141 state_attach_puts ("\n", s);
1142 show_sig_summary (sum, ctx, key, idx, s);
1145 if (key != signature_key)
1146 gpgme_key_unref(key);
1149 return anybad ? 1 : anywarn ? 2 : 0;
1152 /* Do the actual verification step. With IS_SMIME set to true we
1153 assume S/MIME (surprise!) */
1154 static int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
1160 gpgme_data_t signature, message;
1162 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1166 /* We need to tell gpgme about the encoding because the backend can't
1167 auto-detect plain base-64 encoding which is used by S/MIME. */
1169 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1171 err = gpgme_data_new_from_stream(&message, fp);
1173 gpgme_data_release (signature);
1174 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1177 ctx = create_gpgme_context (is_smime);
1179 /* Note: We don't need a current time output because GPGME avoids
1180 such an attack by separating the meta information from the
1182 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1184 err = gpgme_op_verify (ctx, signature, message, NULL);
1185 mutt_need_hard_redraw ();
1189 snprintf (buf, sizeof (buf) - 1,
1190 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1191 state_attach_puts (buf, s);
1193 else { /* Verification succeeded, see what the result is. */
1197 if (signature_key) {
1198 gpgme_key_unref(signature_key);
1199 signature_key = NULL;
1202 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1213 gpgme_verify_result_t result;
1214 gpgme_sig_notation_t notation;
1215 gpgme_signature_t sig;
1217 result = gpgme_op_verify_result (ctx);
1219 for (sig = result->signatures; sig; sig = sig->next) {
1220 if (sig->notations) {
1221 state_attach_puts ("*** Begin Notation (signature by: ", s);
1222 state_attach_puts (sig->fpr, s);
1223 state_attach_puts (") ***\n", s);
1224 for (notation = sig->notations; notation; notation = notation->next)
1226 if (notation->name) {
1227 state_attach_puts (notation->name, s);
1228 state_attach_puts ("=", s);
1230 if (notation->value) {
1231 state_attach_puts (notation->value, s);
1232 if (!(*notation->value
1233 && (notation->value[m_strlen(notation->value) - 1] ==
1235 state_attach_puts ("\n", s);
1238 state_attach_puts ("*** End Notation ***\n", s);
1244 gpgme_release (ctx);
1246 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1248 return badsig ? 1 : anywarn ? 2 : 0;
1251 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1252 IS_SMIME) with body A described further by state S. Write
1253 plaintext out to file FPOUT and return a new body. For PGP returns
1254 a flag in R_IS_SIGNED to indicate whether this is a combined
1255 encrypted and signed message, for S/MIME it returns true when it is
1256 not a encrypted but a signed message. */
1258 decrypt_part(BODY *a, STATE *s, FILE *fpout, int is_smime, int *r_is_signed)
1264 gpgme_data_t ciphertext, plaintext;
1265 int maybe_signed = 0;
1272 ctx = create_gpgme_context (is_smime);
1275 /* Make a data object from the body, create context etc. */
1276 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1279 plaintext = create_gpgme_data ();
1281 /* Do the decryption or the verification in case of the S/MIME hack. */
1282 if ((!is_smime) || maybe_signed) {
1284 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1285 else if (maybe_signed)
1286 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1289 /* Check wether signatures have been verified. */
1290 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1292 if (verify_result->signatures)
1297 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1298 gpgme_data_release (ciphertext);
1300 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1301 /* Check whether this might be a signed message despite what
1302 the mime header told us. Retry then. gpgsm returns the
1303 error information "unsupported Algorithm '?'" but gpgme
1304 will not store this unknown algorithm, thus we test that
1305 it has not been set. */
1306 gpgme_decrypt_result_t result;
1308 result = gpgme_op_decrypt_result (ctx);
1309 if (!result->unsupported_algorithm) {
1311 gpgme_data_release (plaintext);
1315 mutt_need_hard_redraw ();
1316 if ((s->flags & M_DISPLAY)) {
1319 snprintf (buf, sizeof (buf) - 1,
1320 _("[-- Error: decryption failed: %s --]\n\n"),
1321 gpgme_strerror (err));
1322 state_attach_puts (buf, s);
1324 gpgme_data_release (plaintext);
1325 gpgme_release (ctx);
1328 mutt_need_hard_redraw ();
1330 /* Read the output from GPGME, and make sure to change CRLF to LF,
1331 otherwise read_mime_header has a hard time parsing the message. */
1332 if (data_object_to_stream (plaintext, fpout)) {
1333 gpgme_data_release (plaintext);
1334 gpgme_release (ctx);
1337 gpgme_data_release (plaintext);
1339 a->is_signed_data = 0;
1345 a->is_signed_data = 1;
1347 *r_is_signed = -1; /* A signature exists. */
1349 if ((s->flags & M_DISPLAY))
1350 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1351 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1357 if (!anybad && idx && r_is_signed && *r_is_signed)
1358 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1360 if ((s->flags & M_DISPLAY))
1361 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1363 gpgme_release (ctx);
1368 tattach = mutt_read_mime_header (fpout, 0);
1371 * Need to set the length of this body part.
1373 fstat (fileno (fpout), &info);
1374 tattach->length = info.st_size - tattach->offset;
1376 tattach->warnsig = anywarn;
1378 /* See if we need to recurse on this MIME part. */
1379 mutt_parse_part (fpout, tattach);
1385 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1386 the stream in CUR and FPOUT. Returns 0 on success. */
1387 int crypt_pgp_decrypt_mime (FILE * fpin, FILE **fpout, BODY *b, BODY **cur)
1390 BODY *first_part = b;
1393 first_part->goodsig = 0;
1394 first_part->warnsig = 0;
1396 if (!mutt_is_multipart_encrypted(b) || !b->parts || !b->parts->next)
1405 mutt_perror (_("Can't create temporary file"));
1409 *cur = decrypt_part(b, &s, *fpout, 0, &is_signed);
1411 first_part->goodsig = is_signed > 0;
1412 return *cur ? 0 : -1;
1416 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1417 the stream in CUR and FPOUT. Returns 0 on success. */
1418 int crypt_smime_decrypt_mime(FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
1423 long saved_b_offset;
1424 ssize_t saved_b_length;
1427 if (!mutt_is_application_smime (b))
1433 /* Decode the body - we need to pass binary CMS to the
1434 backend. The backend allows for Base64 encoded data but it does
1435 not allow for QP which I have seen in some messages. So better
1437 saved_b_type = b->type;
1438 saved_b_offset = b->offset;
1439 saved_b_length = b->length;
1442 fseeko (s.fpin, b->offset, 0);
1445 mutt_perror (_("Can't create temporary file"));
1450 mutt_decode_attachment (b, &s);
1452 b->length = ftello (s.fpout);
1461 mutt_perror (_("Can't create temporary file"));
1465 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1467 (*cur)->goodsig = is_signed > 0;
1468 b->type = saved_b_type;
1469 b->length = saved_b_length;
1470 b->offset = saved_b_offset;
1473 if (*cur && !is_signed && !(*cur)->parts
1474 && mutt_is_application_smime (*cur)) {
1475 /* Assume that this is a opaque signed s/mime message. This is
1476 an ugly way of doing it but we have anyway a problem with
1477 arbitrary encoded S/MIME messages: Only the outer part may be
1478 encrypted. The entire mime parsing should be revamped,
1479 probably by keeping the temportary files so that we don't
1480 need to decrypt them all the time. Inner parts of an
1481 encrypted part can then pint into this file and tehre won't
1482 never be a need to decrypt again. This needs a partial
1483 rewrite of the MIME engine. */
1487 saved_b_type = bb->type;
1488 saved_b_offset = bb->offset;
1489 saved_b_length = bb->length;
1492 fseeko (s.fpin, bb->offset, 0);
1495 mutt_perror (_("Can't create temporary file"));
1500 mutt_decode_attachment (bb, &s);
1502 bb->length = ftello (s.fpout);
1512 mutt_perror (_("Can't create temporary file"));
1516 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1518 tmp_b->goodsig = is_signed > 0;
1519 bb->type = saved_b_type;
1520 bb->length = saved_b_length;
1521 bb->offset = saved_b_offset;
1524 body_list_wipe(cur);
1527 return *cur ? 0 : -1;
1532 pgp_check_traditional_one_body(FILE *fp, BODY *b, int tagged_only)
1534 char tempfile[_POSIX_PATH_MAX];
1535 char buf[HUGE_STRING];
1542 if (b->type != TYPETEXT)
1545 if (tagged_only && !b->tagged)
1548 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1549 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1554 if ((tfp = fopen(tempfile, "r")) == NULL) {
1559 while (fgets (buf, sizeof (buf), tfp)) {
1560 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1561 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1563 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1573 /* fix the content type */
1575 parameter_setval(&b->parameter, "format", "fixed");
1576 parameter_setval(&b->parameter, "x-action",
1577 enc ? "pgp-encrypted" : "pgp-signed");
1581 int crypt_pgp_check_traditional(FILE *fp, BODY *b, int tagged_only)
1585 for (; b; b = b->next) {
1586 if (is_multipart(b))
1587 rv |= crypt_pgp_check_traditional(fp, b->parts, tagged_only);
1588 if (b->type == TYPETEXT) {
1590 if ((r = mutt_is_application_pgp(b))) {
1593 rv |= pgp_check_traditional_one_body(fp, b, tagged_only);
1602 Copy a clearsigned message, and strip the signature and PGP's
1605 XXX - charset handling: We assume that it is safe to do
1606 character set decoding first, dash decoding second here, while
1607 we do it the other way around in the main handler.
1609 (Note that we aren't worse than Outlook & Cie in this, and also
1610 note that we can successfully handle anything produced by any
1611 existing versions of mutt.) */
1612 static void copy_clearsigned(gpgme_data_t data, STATE * s, char *charset)
1614 char buf[HUGE_STRING];
1615 short complete, armor_header;
1620 fname = data_object_to_tempfile(data, &fp);
1626 fc = fgetconv_open (fp, charset, MCharset.charset, M_ICONV_HOOK_FROM);
1628 for (complete = 1, armor_header = 1;
1629 fgetconvs (buf, sizeof (buf), fc) != NULL;
1630 complete = strchr (buf, '\n') != NULL) {
1633 state_puts (buf, s);
1637 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1647 state_puts (s->prefix, s);
1649 if (buf[0] == '-' && buf[1] == ' ')
1650 state_puts (buf + 2, s);
1652 state_puts (buf, s);
1655 fgetconv_close (&fc);
1659 /* Support for classic_application/pgp */
1660 int crypt_pgp_application_pgp_handler(BODY *m, STATE *s)
1662 int needpass = -1, pgp_keyblock = 0;
1666 off_t last_pos, offset;
1667 char buf[HUGE_STRING];
1668 FILE *pgpout = NULL;
1670 gpgme_error_t err = 0;
1671 gpgme_data_t armored_data = NULL;
1673 short maybe_goodsig = 1;
1674 short have_any_sigs = 0;
1676 char body_charset[STRING]; /* Only used for clearsigned messages. */
1678 /* For clearsigned messages we won't be able to get a character set
1679 but we know that this may only be text thus we assume Latin-1
1681 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1682 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1684 fseeko (s->fpin, m->offset, 0);
1685 last_pos = m->offset;
1687 for (bytes = m->length; bytes > 0;) {
1688 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1691 offset = ftello (s->fpin);
1692 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1695 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1697 start_pos = last_pos;
1699 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1701 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1705 else if (!m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1710 /* XXX - we may wish to recode here */
1712 state_puts (s->prefix, s);
1713 state_puts (buf, s);
1717 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1719 /* Copy PGP material to an data container */
1720 armored_data = create_gpgme_data ();
1721 gpgme_data_write (armored_data, buf, m_strlen(buf));
1722 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1723 offset = ftello (s->fpin);
1724 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1727 gpgme_data_write (armored_data, buf, m_strlen(buf));
1729 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1731 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1732 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1737 /* Invoke PGP if needed */
1738 if (!clearsign || (s->flags & M_VERIFY)) {
1739 unsigned int sig_stat = 0;
1740 gpgme_data_t plaintext;
1743 plaintext = create_gpgme_data ();
1744 ctx = create_gpgme_context (0);
1747 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1749 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1750 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1751 /* Decrypt verify can't handle signed only messages. */
1752 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1753 ? gpgme_error_from_errno (errno) : 0;
1754 /* Must release plaintext so that we supply an
1755 uninitialized object. */
1756 gpgme_data_release (plaintext);
1757 plaintext = create_gpgme_data ();
1758 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1765 snprintf (errbuf, sizeof (errbuf) - 1,
1766 _("Error: decryption/verification failed: %s\n"),
1767 gpgme_strerror (err));
1768 state_attach_puts (errbuf, s);
1770 else { /* Decryption/Verification succeeded */
1774 /* Check wether signatures have been verified. */
1775 gpgme_verify_result_t verify_result;
1777 verify_result = gpgme_op_verify_result (ctx);
1778 if (verify_result->signatures)
1784 if ((s->flags & M_DISPLAY) && sig_stat) {
1789 state_attach_puts (_("[-- Begin signature "
1790 "information --]\n"), s);
1793 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1802 state_attach_puts (_("[-- End signature "
1803 "information --]\n\n"), s);
1806 tmpfname = data_object_to_tempfile(plaintext, &pgpout);
1809 state_attach_puts (_("Error: copy data failed\n"), s);
1813 p_delete(&tmpfname);
1816 gpgme_release (ctx);
1820 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1821 * outputs utf-8 cleartext. This may not always be true, but it
1822 * seems to be a reasonable guess.
1825 if (s->flags & M_DISPLAY) {
1827 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1828 else if (pgp_keyblock)
1829 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1831 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1835 copy_clearsigned (armored_data, s, body_charset);
1842 fc = fgetconv_open (pgpout, "utf-8", MCharset.charset, 0);
1843 while ((c = fgetconv (fc)) != EOF) {
1845 if (c == '\n' && s->prefix)
1846 state_puts (s->prefix, s);
1848 fgetconv_close (&fc);
1851 if (s->flags & M_DISPLAY) {
1852 state_putc ('\n', s);
1854 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1855 else if (pgp_keyblock)
1856 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1858 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1866 /* XXX - we may wish to recode here */
1868 state_puts (s->prefix, s);
1869 state_puts (buf, s);
1873 m->goodsig = (maybe_goodsig && have_any_sigs);
1875 if (needpass == -1) {
1876 state_attach_puts (_("[-- Error: could not find beginning"
1877 " of PGP message! --]\n\n"), s);
1883 /* MIME handler for pgp/mime encrypted messages. */
1884 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1886 char tempfile[_POSIX_PATH_MAX];
1889 BODY *orig_body = a;
1894 if (!a || a->type != TYPEAPPLICATION || !a->subtype
1895 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
1896 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
1897 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
1898 if (s->flags & M_DISPLAY)
1899 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
1904 /* Move forward to the application/pgp-encrypted body. */
1907 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1909 if (s->flags & M_DISPLAY)
1910 state_attach_puts (_("[-- Error: could not create temporary file! "
1915 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
1917 tattach->goodsig = is_signed > 0;
1919 if (s->flags & M_DISPLAY)
1920 state_attach_puts (is_signed ?
1922 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
1923 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
1926 FILE *savefp = s->fpin;
1929 rc = mutt_body_handler (tattach, s);
1934 * if a multipart/signed is the _only_ sub-part of a
1935 * multipart/encrypted, cache signature verification
1938 if (mutt_is_multipart_signed (tattach) && !tattach->next)
1939 orig_body->goodsig |= tattach->goodsig;
1941 if (s->flags & M_DISPLAY) {
1942 state_puts ("\n", s);
1943 state_attach_puts (is_signed ?
1945 ("[-- End of PGP/MIME signed and encrypted data --]\n")
1946 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
1949 body_list_wipe(&tattach);
1953 mutt_unlink (tempfile);
1957 /* Support for application/smime */
1958 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
1960 char tempfile[_POSIX_PATH_MAX];
1967 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1969 if (s->flags & M_DISPLAY)
1970 state_attach_puts (_("[-- Error: could not create temporary file! "
1975 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
1977 tattach->goodsig = is_signed > 0;
1979 if (s->flags & M_DISPLAY)
1980 state_attach_puts (is_signed ?
1981 _("[-- The following data is S/MIME signed --]\n\n") :
1982 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
1985 FILE *savefp = s->fpin;
1988 rc = mutt_body_handler (tattach, s);
1993 * if a multipart/signed is the _only_ sub-part of a
1994 * multipart/encrypted, cache signature verification
1997 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
1998 if (!(a->goodsig = tattach->goodsig))
1999 a->warnsig = tattach->warnsig;
2001 else if (tattach->goodsig) {
2003 a->warnsig = tattach->warnsig;
2006 if (s->flags & M_DISPLAY) {
2007 state_puts ("\n", s);
2008 state_attach_puts (is_signed ?
2009 _("[-- End of S/MIME signed data --]\n") :
2010 _("[-- End of S/MIME encrypted data --]\n"), s);
2013 body_list_wipe(&tattach);
2017 mutt_unlink (tempfile);
2023 * Format an entry on the CRYPT key selection menu.
2026 * %k key id %K key id of the principal key
2028 * %a algorithm %A algorithm of the princ. key
2029 * %l length %L length of the princ. key
2030 * %f flags %F flags of the princ. key
2031 * %c capabilities %C capabilities of the princ. key
2032 * %t trust/validity of the key-uid association
2034 * %[...] date of key using strftime(3)
2038 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
2039 const char *src, const char *prefix,
2040 const char *ifstr, const char *elstr,
2041 anytype data, format_flag flags)
2044 crypt_entry_t *entry;
2047 int optional = (flags & M_FORMAT_OPTIONAL);
2048 const char *s = NULL;
2054 /* if (isupper ((unsigned char) op)) */
2057 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
2060 switch (ascii_tolower (op)) {
2064 char buf2[STRING], *p;
2080 while (len > 0 && *cp != ']') {
2089 break; /* not enough space */
2099 if (do_locales && Locale)
2100 setlocale (LC_TIME, Locale);
2105 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2106 tt = key->kobj->subkeys->timestamp;
2108 tm = localtime (&tt);
2110 strftime (buf2, sizeof (buf2), dest, tm);
2113 setlocale (LC_TIME, "C");
2115 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2116 snprintf (dest, destlen, fmt, buf2);
2123 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2124 snprintf (dest, destlen, fmt, entry->num);
2129 /* fixme: we need a way to distinguish between main and subkeys.
2130 Store the idx in entry? */
2131 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2132 snprintf (dest, destlen, fmt, crypt_keyid (key));
2137 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2138 snprintf (dest, destlen, fmt, key->uid);
2143 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2144 if (key->kobj->subkeys)
2145 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2148 snprintf (dest, destlen, fmt, s);
2153 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2154 if (key->kobj->subkeys)
2155 val = key->kobj->subkeys->length;
2158 snprintf (dest, destlen, fmt, val);
2163 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2164 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2166 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2171 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2172 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2174 else if (!(kflags & (KEYFLAG_ABILITIES)))
2178 if ((kflags & KEYFLAG_ISX509))
2181 gpgme_user_id_t uid = NULL;
2184 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2185 i++, uid = uid->next);
2187 switch (uid->validity) {
2188 case GPGME_VALIDITY_UNDEFINED:
2191 case GPGME_VALIDITY_NEVER:
2194 case GPGME_VALIDITY_MARGINAL:
2197 case GPGME_VALIDITY_FULL:
2200 case GPGME_VALIDITY_ULTIMATE:
2203 case GPGME_VALIDITY_UNKNOWN:
2209 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2210 snprintf (dest, destlen, fmt, s ? *s : 'B');
2213 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2214 snprintf (dest, destlen, fmt,
2215 gpgme_get_protocol_name (key->kobj->protocol));
2222 if (flags & M_FORMAT_OPTIONAL)
2223 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2224 mutt_attach_fmt, data, 0);
2228 /* Used by the display fucntion to format a line. */
2229 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2231 cryptkey_t **cryptkey_table = (cryptkey_t **) menu->data;
2232 crypt_entry_t entry;
2234 entry.key = cryptkey_table[num];
2235 entry.num = num + 1;
2237 m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
2238 option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
2241 /* Compare two addresses and the keyid to be used for sorting. */
2242 static int _crypt_compare_address (const void *a, const void *b)
2244 cryptkey_t **s = (cryptkey_t **) a;
2245 cryptkey_t **t = (cryptkey_t **) b;
2248 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2251 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2254 static int crypt_compare_address (const void *a, const void *b)
2256 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2257 : _crypt_compare_address (a, b));
2261 /* Compare two key IDs and the addresses to be used for sorting. */
2262 static int _crypt_compare_keyid (const void *a, const void *b)
2264 cryptkey_t **s = (cryptkey_t **) a;
2265 cryptkey_t **t = (cryptkey_t **) b;
2268 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2271 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2274 static int crypt_compare_keyid (const void *a, const void *b)
2276 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2277 : _crypt_compare_keyid (a, b));
2280 /* Compare 2 creation dates and the addresses. For sorting. */
2281 static int _crypt_compare_date (const void *a, const void *b)
2283 cryptkey_t **s = (cryptkey_t **) a;
2284 cryptkey_t **t = (cryptkey_t **) b;
2285 unsigned long ts = 0, tt = 0;
2287 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2288 ts = (*s)->kobj->subkeys->timestamp;
2289 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2290 tt = (*t)->kobj->subkeys->timestamp;
2297 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2300 static int crypt_compare_date (const void *a, const void *b)
2302 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2303 : _crypt_compare_date (a, b));
2306 /* Compare two trust values, the key length, the creation dates. the
2307 addresses and the key IDs. For sorting. */
2308 static int _crypt_compare_trust (const void *a, const void *b)
2310 cryptkey_t **s = (cryptkey_t **) a;
2311 cryptkey_t **t = (cryptkey_t **) b;
2312 unsigned long ts = 0, tt = 0;
2315 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2316 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2319 if ((*s)->kobj->uids)
2320 ts = (*s)->kobj->uids->validity;
2321 if ((*t)->kobj->uids)
2322 tt = (*t)->kobj->uids->validity;
2323 if ((r = (tt - ts)))
2326 if ((*s)->kobj->subkeys)
2327 ts = (*s)->kobj->subkeys->length;
2328 if ((*t)->kobj->subkeys)
2329 tt = (*t)->kobj->subkeys->length;
2333 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2334 ts = (*s)->kobj->subkeys->timestamp;
2335 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2336 tt = (*t)->kobj->subkeys->timestamp;
2342 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2344 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2347 static int crypt_compare_trust (const void *a, const void *b)
2349 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2350 : _crypt_compare_trust (a, b));
2353 /* Print the X.500 Distinguished Name part KEY from the array of parts
2355 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2359 for (; dn->key; dn++) {
2360 if (!m_strcmp(dn->key, key)) {
2363 print_utf8 (fp, dn->value, m_strlen(dn->value));
2370 /* Print all parts of a DN in a standard sequence. */
2371 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2373 const char *stdpart[] = {
2374 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2376 int any = 0, any2 = 0, i;
2378 for (i = 0; stdpart[i]; i++) {
2381 any = print_dn_part (fp, dn, stdpart[i]);
2383 /* now print the rest without any specific ordering */
2384 for (; dn->key; dn++) {
2385 for (i = 0; stdpart[i]; i++) {
2386 if (!m_strcmp(dn->key, stdpart[i]))
2394 any = print_dn_part (fp, dn, dn->key);
2403 /* Parse an RDN; this is a helper to parse_dn(). */
2404 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2405 const unsigned char *string)
2407 const unsigned char *s, *s1;
2411 /* parse attributeType */
2412 for (s = string + 1; *s && *s != '='; s++);
2414 return NULL; /* error */
2417 return NULL; /* empty key */
2418 array->key = p_dupstr(string, n );
2419 p = (unsigned char *) array->key;
2422 if (*string == '#') { /* hexstring */
2424 for (s = string; hexval(*s) >= 0; s++)
2428 return NULL; /* empty or odd number of digits */
2430 p = p_new(unsigned char, n + 1);
2431 array->value = (char *) p;
2432 for (s1 = string; n; s1 += 2, n--)
2433 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2436 else { /* regular v3 quoted string */
2437 for (n = 0, s = string; *s; s++) {
2438 if (*s == '\\') { /* pair */
2440 if (*s == ',' || *s == '=' || *s == '+'
2441 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2442 || *s == '\\' || *s == '"' || *s == ' ')
2444 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2449 return NULL; /* invalid escape sequence */
2452 return NULL; /* invalid encoding */
2453 else if (*s == ',' || *s == '=' || *s == '+'
2454 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2460 p = p_new(unsigned char, n + 1);
2461 array->value = (char *) p;
2462 for (s = string; n; s++, n--) {
2465 if (hexval(*s) >= 0) {
2466 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2481 /* Parse a DN and return an array-ized one. This is not a validating
2482 parser and it does not support any old-stylish syntax; gpgme is
2483 expected to return only rfc2253 compatible strings. */
2484 static struct dn_array_s *parse_dn (const unsigned char *string)
2486 struct dn_array_s *array;
2487 ssize_t arrayidx, arraysize;
2490 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2491 array = p_new(struct dn_array_s, arraysize + 1);
2494 while (*string == ' ')
2498 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2499 struct dn_array_s *a2;
2502 a2 = p_new(struct dn_array_s, arraysize + 1);
2503 for (i = 0; i < arrayidx; i++) {
2504 a2[i].key = array[i].key;
2505 a2[i].value = array[i].value;
2510 array[arrayidx].key = NULL;
2511 array[arrayidx].value = NULL;
2512 string = parse_dn_part (array + arrayidx, string);
2516 while (*string == ' ')
2518 if (*string && *string != ',' && *string != ';' && *string != '+')
2519 goto failure; /* invalid delimiter */
2523 array[arrayidx].key = NULL;
2524 array[arrayidx].value = NULL;
2528 for (i = 0; i < arrayidx; i++) {
2529 p_delete(&array[i].key);
2530 p_delete(&array[i].value);
2537 /* Print a nice representation of the USERID and make sure it is
2538 displayed in a proper way, which does mean to reorder some parts
2539 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2540 functions. It is utf-8 encoded. */
2541 static void parse_and_print_user_id(FILE * fp, const char *userid)
2546 if (*userid == '<') {
2547 s = strchr (userid + 1, '>');
2549 print_utf8 (fp, userid + 1, s - userid - 1);
2551 else if (*userid == '(')
2552 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2553 else if (*userid & ~127 || __m_strdigits[(int)*userid] == 255)
2554 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2556 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2559 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2561 print_dn_parts (fp, dn);
2562 for (i = 0; dn[i].key; i++) {
2563 p_delete(&dn[i].key);
2564 p_delete(&dn[i].value);
2572 KEY_CAP_CAN_ENCRYPT,
2577 static unsigned int key_check_cap(gpgme_key_t key, key_cap_t cap)
2579 gpgme_subkey_t subkey = NULL;
2580 unsigned int ret = 0;
2583 case KEY_CAP_CAN_ENCRYPT:
2584 if (!(ret = key->can_encrypt))
2585 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2586 if ((ret = subkey->can_encrypt))
2589 case KEY_CAP_CAN_SIGN:
2590 if (!(ret = key->can_sign))
2591 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2592 if ((ret = subkey->can_sign))
2595 case KEY_CAP_CAN_CERTIFY:
2596 if (!(ret = key->can_certify))
2597 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2598 if ((ret = subkey->can_certify))
2607 /* Print verbose information about a key or certificate to FP. */
2608 static void print_key_info (gpgme_key_t key, FILE * fp)
2611 const char *s = NULL, *s2 = NULL;
2614 char shortbuf[STRING];
2615 unsigned long aval = 0;
2619 gpgme_user_id_t uid = NULL;
2622 setlocale (LC_TIME, Locale);
2624 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2626 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2631 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2634 fputs (_("[Invalid]"), fp);
2638 print_utf8 (fp, s, m_strlen(s));
2640 parse_and_print_user_id (fp, s);
2644 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2645 tt = key->subkeys->timestamp;
2647 tm = localtime (&tt);
2648 #ifdef HAVE_LANGINFO_D_T_FMT
2649 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2651 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2653 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2656 if (key->subkeys && (key->subkeys->expires > 0)) {
2657 tt = key->subkeys->expires;
2659 tm = localtime (&tt);
2660 #ifdef HAVE_LANGINFO_D_T_FMT
2661 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2663 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2665 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2669 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2673 s2 = is_pgp ? "PGP" : "X.509";
2676 aval = key->subkeys->length;
2678 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2680 fprintf (fp, _("Key Usage .: "));
2683 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2684 fprintf (fp, "%s%s", delim, _("encryption"));
2687 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2688 fprintf (fp, "%s%s", delim, _("signing"));
2691 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2692 fprintf (fp, "%s%s", delim, _("certification"));
2698 s = key->subkeys->fpr;
2699 fputs (_("Fingerprint: "), fp);
2700 if (is_pgp && m_strlen(s) == 40) {
2701 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2706 putc (is_pgp ? ' ' : ':', fp);
2707 if (is_pgp && i == 4)
2712 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2715 putc (is_pgp ? ' ' : ':', fp);
2716 if (is_pgp && i == 7)
2720 fprintf (fp, "%s\n", s);
2723 if (key->issuer_serial) {
2724 s = key->issuer_serial;
2726 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2729 if (key->issuer_name) {
2730 s = key->issuer_name;
2732 fprintf (fp, _("Issued By .: "));
2733 parse_and_print_user_id (fp, s);
2738 /* For PGP we list all subkeys. */
2740 gpgme_subkey_t subkey = NULL;
2742 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2746 if (m_strlen(s) == 16)
2747 s += 8; /* display only the short keyID */
2748 fprintf (fp, _("Subkey ....: 0x%s"), s);
2749 if (subkey->revoked) {
2751 fputs (_("[Revoked]"), fp);
2753 if (subkey->invalid) {
2755 fputs (_("[Invalid]"), fp);
2757 if (subkey->expired) {
2759 fputs (_("[Expired]"), fp);
2761 if (subkey->disabled) {
2763 fputs (_("[Disabled]"), fp);
2767 if (subkey->timestamp > 0) {
2768 tt = subkey->timestamp;
2770 tm = localtime (&tt);
2771 #ifdef HAVE_LANGINFO_D_T_FMT
2772 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2774 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2776 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2779 if (subkey->expires > 0) {
2780 tt = subkey->expires;
2782 tm = localtime (&tt);
2783 #ifdef HAVE_LANGINFO_D_T_FMT
2784 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2786 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2788 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2792 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2797 aval = subkey->length;
2801 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2803 fprintf (fp, _("Key Usage .: "));
2806 if (subkey->can_encrypt) {
2807 fprintf (fp, "%s%s", delim, _("encryption"));
2810 if (subkey->can_sign) {
2811 fprintf (fp, "%s%s", delim, _("signing"));
2814 if (subkey->can_certify) {
2815 fprintf (fp, "%s%s", delim, _("certification"));
2823 setlocale (LC_TIME, "C");
2827 /* Show detailed information about the selected key */
2828 static void verify_key (cryptkey_t * key)
2831 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2833 gpgme_ctx_t listctx = NULL;
2835 gpgme_key_t k = NULL;
2838 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2840 mutt_perror (_("Can't create temporary file"));
2843 mutt_message _("Collecting data...");
2845 print_key_info (key->kobj, fp);
2847 err = gpgme_new (&listctx);
2849 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2850 gpgme_strerror (err));
2853 if ((key->flags & KEYFLAG_ISX509))
2854 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2858 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2860 err = gpgme_op_keylist_start (listctx, s, 0);
2864 err = gpgme_op_keylist_next (listctx, &k);
2866 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2869 gpgme_op_keylist_end (listctx);
2871 print_key_info (k, fp);
2874 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2881 gpgme_release (listctx);
2883 mutt_clear_error ();
2884 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2885 mutt_pager(cmd, tempfile, 0, NULL);
2888 /* Implementation of `findkeys'. */
2890 static void add_hints(string_array *arr, const char *s)
2896 int l = strcspn(s, " ,.:\"()<>\n");
2897 string_array_append(arr, p_dupstr(s, l));
2899 s += strspn(s, " ,.:\"()<>\n");
2903 /* Return a list of keys which are candidates for the selection. */
2905 get_candidates(string_array *hints, unsigned int app, int secret)
2907 cryptkey_t *res = NULL, **kend = &res;
2912 if (hints->len <= 0)
2914 string_array_append(hints, NULL);
2915 ctx = create_gpgme_context(0);
2917 if ((app & APPLICATION_PGP)) {
2918 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2921 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2922 gpgme_strerror(err));
2927 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2928 gpgme_user_id_t uid = NULL;
2929 unsigned int flags = 0;
2932 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2933 flags |= KEYFLAG_CANENCRYPT;
2934 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2935 flags |= KEYFLAG_CANSIGN;
2937 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2938 cryptkey_t *k = p_new(cryptkey_t, 1);
2947 if (gpg_err_code(err) != GPG_ERR_EOF)
2948 mutt_error(_("gpgme_op_keylist_next failed: %s"), gpgme_strerror(err));
2949 gpgme_op_keylist_end(ctx);
2952 if ((app & APPLICATION_SMIME)) {
2953 gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
2954 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2957 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2958 gpgme_strerror(err));
2963 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2964 gpgme_user_id_t uid = NULL;
2965 unsigned int flags = KEYFLAG_ISX509;
2968 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2969 flags |= KEYFLAG_CANENCRYPT;
2970 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2971 flags |= KEYFLAG_CANSIGN;
2973 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2974 cryptkey_t *k = p_new(cryptkey_t, 1);
2983 if (gpg_err_code(err) != GPG_ERR_EOF)
2984 mutt_error(_("gpgme_op_keylist_next failed: %s"),
2985 gpgme_strerror(err));
2986 gpgme_op_keylist_end(ctx);
2993 /* Display a menu to select a key from the array KEYS. FORCED_VALID
2994 will be set to true on return if the user did override the the
2996 static cryptkey_t *crypt_select_key (cryptkey_t * keys,
2997 address_t * p, const char *s,
2998 unsigned int app, int *forced_valid)
3001 cryptkey_t **cryptkey_table;
3004 char helpstr[STRING], buf[LONG_STRING];
3006 int (*f) (const void *, const void *);
3007 int menu_to_use = 0;
3012 /* build the key table */
3014 cryptkey_table = NULL;
3015 for (k = keys; k; k = k->next) {
3016 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
3023 p_realloc(&cryptkey_table, keymax);
3026 cryptkey_table[i++] = k;
3029 if (!i && unusable) {
3030 mutt_error _("All matching keys are marked expired/revoked.");
3036 switch (PgpSortKeys & SORT_MASK) {
3038 f = crypt_compare_date;
3041 f = crypt_compare_keyid;
3044 f = crypt_compare_address;
3048 f = crypt_compare_trust;
3051 qsort (cryptkey_table, i, sizeof (cryptkey_t *), f);
3053 if (app & APPLICATION_PGP)
3054 menu_to_use = MENU_KEY_SELECT_PGP;
3055 else if (app & APPLICATION_SMIME)
3056 menu_to_use = MENU_KEY_SELECT_SMIME;
3059 mutt_make_help (buf, sizeof (buf), _("Exit "), menu_to_use, OP_EXIT);
3060 m_strcat(helpstr, sizeof(helpstr), buf);
3061 mutt_make_help (buf, sizeof (buf), _("Select "), menu_to_use,
3062 OP_GENERIC_SELECT_ENTRY);
3063 m_strcat(helpstr, sizeof(helpstr), buf);
3064 mutt_make_help (buf, sizeof (buf), _("Check key "),
3065 menu_to_use, OP_VERIFY_KEY);
3066 m_strcat(helpstr, sizeof(helpstr), buf);
3067 mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
3068 m_strcat(helpstr, sizeof(helpstr), buf);
3070 menu = mutt_new_menu ();
3072 menu->make_entry = crypt_entry;
3073 menu->menu = menu_to_use;
3074 menu->help = helpstr;
3075 menu->data = cryptkey_table;
3080 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3081 ts = _("PGP and S/MIME keys matching");
3082 else if ((app & APPLICATION_PGP))
3083 ts = _("PGP keys matching");
3084 else if ((app & APPLICATION_SMIME))
3085 ts = _("S/MIME keys matching");
3087 ts = _("keys matching");
3090 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3092 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3096 mutt_clear_error ();
3100 switch (mutt_menuLoop (menu)) {
3102 verify_key (cryptkey_table[menu->current]);
3103 menu->redraw = REDRAW_FULL;
3107 mutt_message ("%s", cryptkey_table[menu->current]->uid);
3110 case OP_GENERIC_SELECT_ENTRY:
3111 /* FIXME make error reporting more verbose - this should be
3112 easy because gpgme provides more information */
3113 if (option (OPTPGPCHECKTRUST)) {
3114 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE ) {
3115 mutt_error(_("This key can't be used: "
3116 "expired/disabled/revoked."));
3121 if (option (OPTPGPCHECKTRUST) &&
3122 ((cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3123 || !crypt_id_is_strong (cryptkey_table[menu->current]))) {
3125 char buff[LONG_STRING];
3127 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3128 s = N_("ID is expired/disabled/revoked.");
3130 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3131 gpgme_user_id_t uid = NULL;
3136 uid = cryptkey_table[menu->current]->kobj->uids;
3137 for (j = 0; (j < cryptkey_table[menu->current]->idx) && uid;
3138 j++, uid = uid->next);
3140 val = uid->validity;
3143 case GPGME_VALIDITY_UNKNOWN:
3144 case GPGME_VALIDITY_UNDEFINED:
3145 warn_s = N_("ID has undefined validity.");
3147 case GPGME_VALIDITY_NEVER:
3148 warn_s = N_("ID is not valid.");
3150 case GPGME_VALIDITY_MARGINAL:
3151 warn_s = N_("ID is only marginally valid.");
3153 case GPGME_VALIDITY_FULL:
3154 case GPGME_VALIDITY_ULTIMATE:
3158 snprintf (buff, sizeof (buff),
3159 _("%s Do you really want to use the key?"), _(warn_s));
3161 if (mutt_yesorno (buff, 0) != 1) {
3162 mutt_clear_error ();
3169 k = cryptkey_dup(cryptkey_table[menu->current]);
3180 mutt_menuDestroy (&menu);
3181 p_delete(&cryptkey_table);
3183 set_option (OPTNEEDREDRAW);
3189 crypt_getkeybyaddr(address_t * a, int abilities, int app, int *forced_valid)
3196 int this_key_has_strong;
3197 int this_key_has_weak;
3198 int this_key_has_invalid;
3201 cryptkey_t *keys, *k;
3202 cryptkey_t *the_valid_key = NULL;
3203 cryptkey_t *matches = NULL;
3204 cryptkey_t **matches_endp = &matches;
3210 string_array_init(&hints);
3211 add_hints(&hints, a->mailbox);
3212 add_hints(&hints, a->personal);
3214 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3215 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3216 string_array_wipe(&hints);
3222 for (k = keys; k; k = k->next) {
3223 if (abilities && !(k->flags & abilities)) {
3227 this_key_has_weak = 0; /* weak but valid match */
3228 this_key_has_invalid = 0; /* invalid match */
3229 this_key_has_strong = 0; /* strong and valid match */
3230 match = 0; /* any match */
3232 r = rfc822_parse_adrlist (NULL, k->uid);
3233 for (p = r; p; p = p->next) {
3234 int validity = crypt_id_matches_addr (a, p, k);
3236 if (validity & CRYPT_KV_MATCH) /* something matches */
3239 /* is this key a strong candidate? */
3240 if ((validity & CRYPT_KV_VALID)
3241 && (validity & CRYPT_KV_STRONGID)
3242 && (validity & CRYPT_KV_ADDR)) {
3243 if (the_valid_key && the_valid_key != k)
3246 this_key_has_strong = 1;
3248 else if ((validity & CRYPT_KV_MATCH)
3249 && !(validity & CRYPT_KV_VALID))
3250 this_key_has_invalid = 1;
3251 else if ((validity & CRYPT_KV_MATCH)
3252 && (!(validity & CRYPT_KV_STRONGID)
3253 || !(validity & CRYPT_KV_ADDR)))
3254 this_key_has_weak = 1;
3256 address_list_wipe(&r);
3261 if (!this_key_has_strong && this_key_has_invalid)
3263 if (!this_key_has_strong && this_key_has_weak)
3266 *matches_endp = tmp = cryptkey_dup(k);
3267 matches_endp = &tmp->next;
3268 the_valid_key = tmp;
3271 key_list_wipe(&keys);
3274 if (the_valid_key && !multi && !weak
3275 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3277 * There was precisely one strong match on a valid ID, there
3278 * were no valid keys with weak matches, and we aren't
3279 * interested in seeing invalid keys.
3281 * Proceed without asking the user.
3283 k = cryptkey_dup(the_valid_key);
3286 * Else: Ask the user.
3288 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3290 key_list_wipe(&matches);
3300 crypt_getkeybystr(const char *p, int abilities, int app, int *forced_valid)
3303 cryptkey_t *matches = NULL;
3304 cryptkey_t **matches_endp = &matches;
3308 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3314 string_array_init(&hints);
3315 add_hints(&hints, p);
3316 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3317 string_array_wipe(&hints);
3323 for (k = keys; k; k = k->next) {
3324 const char *s = crypt_keyid(k);
3326 if (abilities && !(k->flags & abilities))
3331 if (!*p || !m_strcasecmp(p, s)
3332 || (!m_strncasecmp(p, "0x", 2) && !m_strcasecmp(p + 2, s))
3333 || m_stristr(k->uid, p))
3337 *matches_endp = tmp = cryptkey_dup(k);
3338 matches_endp = &tmp->next;
3341 key_list_wipe(&keys);
3344 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3345 key_list_wipe(&matches);
3352 /* Display TAG as a prompt to ask for a key.
3353 * ABILITIES describe the required key abilities (sign, encrypt) and APP the
3354 * type of the requested key; ether S/MIME or PGP.
3355 * Return a copy of the key or NULL if not found. */
3357 crypt_ask_for_key(const char *tag, int abilities, int app, int *forced_valid)
3364 forced_valid = &dummy;
3370 if (mutt_get_field(tag, resp, sizeof(resp), M_CLEAR) != 0)
3373 if (m_strisempty(resp))
3376 if ((key = crypt_getkeybystr(resp, abilities, app, forced_valid)))
3383 /* This routine attempts to find the keyids of the recipients of a
3384 message. It returns NULL if any of the keys can not be found. */
3385 static char *find_keys(ENVELOPE *env, unsigned int app)
3387 address_t *lst = NULL, *addr;
3388 buffer_t *keylist = buffer_new();
3391 address_t **last = &lst;
3392 *last = address_list_dup(env->to);
3393 last = address_list_last(last);
3394 *last = address_list_dup(env->cc);
3395 last = address_list_last(last);
3396 *last = address_list_dup(env->bcc);
3398 rfc822_qualify(lst, mutt_fqdn(1));
3399 address_list_uniq(lst);
3402 while ((addr = address_list_pop(&lst))) {
3404 int forced_valid = 0;
3406 cryptkey_t *key = NULL;
3408 if ((keyID = mutt_crypt_hook(addr))) {
3411 snprintf(buf, sizeof(buf), _("Use keyID = \"%s\" for %s?"), keyID,
3413 r = mutt_yesorno(buf, M_YES);
3416 address_list_wipe(&lst);
3417 address_list_wipe(&addr);
3418 buffer_delete(&keylist);
3424 /* check for e-mail address */
3425 if (strchr(keyID, '@') && (a = rfc822_parse_adrlist(NULL, keyID))) {
3426 rfc822_qualify(a, mutt_fqdn(1));
3427 address_list_wipe(&addr);
3430 key = crypt_getkeybystr(keyID, KEYFLAG_CANENCRYPT, app,
3437 key = crypt_getkeybyaddr(addr, KEYFLAG_CANENCRYPT, app, &forced_valid);
3440 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), addr->mailbox);
3441 key = crypt_ask_for_key(buf, KEYFLAG_CANENCRYPT, app,
3444 address_list_wipe(&lst);
3445 address_list_wipe(&addr);
3446 buffer_delete(&keylist);
3452 buffer_addch(keylist, ' ');
3453 buffer_addstr(keylist, "0x");
3454 buffer_addstr(keylist, crypt_fpr(key));
3456 buffer_addch(keylist, '!');
3458 key_list_wipe(&key);
3459 address_list_wipe(&addr);
3462 address_list_wipe(&lst);
3463 return buffer_unwrap(&keylist);
3466 int crypt_get_keys(HEADER *msg, char **keylist)
3468 /* Do a quick check to make sure that we can find all of the encryption
3469 * keys if the user has requested this service.
3474 if (msg->security & ENCRYPT) {
3475 if (msg->security & APPLICATION_PGP) {
3476 set_option(OPTPGPCHECKTRUST);
3477 *keylist = find_keys(msg->env, APPLICATION_PGP);
3478 unset_option(OPTPGPCHECKTRUST);
3483 if (msg->security & APPLICATION_SMIME) {
3484 *keylist = find_keys(msg->env, APPLICATION_SMIME);
3494 int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
3500 if (msg->security & APPLICATION_SMIME)
3502 if (msg->security & APPLICATION_PGP)
3506 ? mutt_multi_choice(_("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3508 : mutt_multi_choice(_("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3512 case 1: /* (e)ncrypt */
3513 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3514 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3517 case 2: /* (s)ign */
3518 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3519 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3522 case 3: /* sign (a)s */
3523 p = crypt_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN,
3524 is_smime ? APPLICATION_SMIME : APPLICATION_PGP,
3527 snprintf(buf, sizeof(buf), "0x%s", crypt_keyid(p));
3528 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs, buf);
3530 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3532 *redraw = REDRAW_FULL;
3535 case 4: /* (b)oth */
3537 msg->security = SMIMEENCRYPT | SMIMESIGN;
3539 msg->security = PGPENCRYPT | PGPSIGN;
3543 case 5: /* (p)gp or s/(m)ime */
3544 is_smime = !is_smime;
3547 case 6: /* (c)lear */
3548 return msg->security = 0;
3552 msg->security &= ~APPLICATION_PGP;
3553 msg->security |= APPLICATION_SMIME;
3555 msg->security &= ~APPLICATION_SMIME;
3556 msg->security |= APPLICATION_PGP;
3559 return msg->security;
3562 int crypt_smime_verify_sender(HEADER *h)
3564 address_t *sender = NULL;
3565 unsigned int ret = 1;
3568 h->env->from = mutt_expand_aliases(h->env->from);
3569 sender = h->env->from;
3570 } else if (h->env->sender) {
3571 h->env->sender = mutt_expand_aliases (h->env->sender);
3572 sender = h->env->sender;
3576 mutt_any_key_to_continue ("Failed to figure out sender");
3580 if (signature_key) {
3581 gpgme_key_t key = signature_key;
3582 gpgme_user_id_t uid = NULL;
3583 int sender_length = 0;
3586 sender_length = m_strlen(sender->mailbox);
3587 for (uid = key->uids; uid && ret; uid = uid->next) {
3588 uid_length = m_strlen(uid->email);
3589 if (1 && (uid->email[0] == '<')
3590 && (uid->email[uid_length - 1] == '>')
3591 && (uid_length == sender_length + 2)
3592 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3596 mutt_any_key_to_continue ("Failed to verify sender");
3600 if (signature_key) {
3601 gpgme_key_unref(signature_key);
3602 signature_key = NULL;
3607 static void crypt_invoke_import(FILE *stream, int smime)
3609 gpgme_ctx_t ctx = create_gpgme_context(smime);
3613 err = gpgme_data_new_from_stream(&data, stream);
3615 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3620 err = gpgme_op_import(ctx, data);
3622 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3623 gpgme_data_release(data);
3628 gpgme_data_release(data);
3633 static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
3636 FILE *tmpfp = tmpfile();
3638 if (tmpfp == NULL) {
3639 mutt_perror (_("Can't create temporary file"));
3646 mutt_body_handler(top, &s);
3649 crypt_invoke_import(tmpfp, 0);
3653 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3657 for (; top; top = top->next) {
3658 if (!tag || top->tagged)
3659 pgp_extract_keys_from_attachment (fp, top);
3666 void crypt_invoke_message (int type)
3668 if (type & APPLICATION_PGP) {
3669 mutt_message _("Invoking PGP...");
3671 else if (type & APPLICATION_SMIME) {
3672 mutt_message _("Invoking S/MIME...");
3676 int mutt_protect (HEADER * msg, char *keylist)
3678 BODY *pbody = NULL, *tmp_pbody = NULL;
3679 BODY *tmp_smime_pbody = NULL;
3680 BODY *tmp_pgp_pbody = NULL;
3681 int flags = msg->security;
3686 tmp_smime_pbody = msg->content;
3687 tmp_pgp_pbody = msg->content;
3689 if (msg->security & SIGN) {
3690 if (msg->security & APPLICATION_SMIME) {
3691 if (!(tmp_pbody = sign_message(msg->content, 1)))
3693 pbody = tmp_smime_pbody = tmp_pbody;
3696 if ((msg->security & APPLICATION_PGP)
3697 && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
3698 if (!(tmp_pbody = sign_message(msg->content, 0)))
3702 pbody = tmp_pgp_pbody = tmp_pbody;
3705 if ((msg->security & APPLICATION_SMIME)
3706 && (msg->security & APPLICATION_PGP)) {
3707 /* here comes the draft ;-) */
3712 if (msg->security & ENCRYPT) {
3713 if ((msg->security & APPLICATION_SMIME)) {
3714 if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
3716 /* signed ? free it! */
3719 /* free tmp_body if messages was signed AND encrypted ... */
3720 if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
3721 /* detatch and dont't delete msg->content,
3722 which tmp_smime_pbody->parts after signing. */
3723 tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
3724 msg->content->next = NULL;
3725 body_list_wipe(&tmp_smime_pbody);
3730 if ((msg->security & APPLICATION_PGP)) {
3731 if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
3734 /* did we perform a retainable signature? */
3735 if (flags != msg->security) {
3736 /* remove the outer multipart layer */
3737 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3738 /* get rid of the signature */
3739 body_list_wipe(&tmp_pgp_pbody->next);
3745 /* destroy temporary signature envelope when doing retainable
3749 if (flags != msg->security) {
3750 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3751 body_list_wipe(&tmp_pgp_pbody->next);
3757 msg->content = pbody;
3763 int crypt_query (BODY * m)
3770 if (m->type == TYPEAPPLICATION) {
3771 t |= mutt_is_application_pgp (m);
3773 t |= mutt_is_application_smime (m);
3774 if (t && m->goodsig)
3779 else if (m->type == TYPETEXT) {
3780 t |= mutt_is_application_pgp (m);
3781 if (t && m->goodsig)
3785 if (m->type == TYPEMULTIPART) {
3786 t |= mutt_is_multipart_encrypted (m);
3787 t |= mutt_is_multipart_signed (m);
3789 if (t && m->goodsig)
3793 if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
3797 u = m->parts ? ~0 : 0; /* Bits set in all parts */
3798 w = 0; /* Bits set in any part */
3800 for (p = m->parts; p; p = p->next) {
3801 v = crypt_query (p);
3805 t |= u | (w & ~GOODSIGN);
3807 if ((w & GOODSIGN) && !(u & GOODSIGN))
3815 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
3821 fseeko (s->fpin, a->hdr_offset, 0);
3822 bytes = a->length + a->offset - a->hdr_offset;
3825 if ((c = fgetc (s->fpin)) == EOF)
3833 if (c == '\n' && !hadcr)
3842 static void extract_keys_aux(FILE *fpout, HEADER *h)
3844 mutt_parse_mime_message (Context, h);
3847 if (h->security & APPLICATION_PGP) {
3848 mutt_copy_message(fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
3851 mutt_endwin (_("Trying to extract PGP keys...\n"));
3854 if (h->security & APPLICATION_SMIME) {
3855 if (h->security & ENCRYPT)
3856 mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
3857 | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
3859 mutt_copy_message(fpout, Context, h, 0, 0);
3862 mutt_message (_("Trying to extract S/MIME certificates...\n"));
3866 crypt_invoke_import(fpout, h->security & APPLICATION_SMIME);
3869 void crypt_extract_keys_from_messages(HEADER * h)
3871 FILE *tmpfp = tmpfile();
3873 mutt_error(_("Could not create temporary file"));
3879 for (i = 0; i < Context->vcount; i++) {
3880 if (!Context->hdrs[Context->v2r[i]]->tagged)
3882 extract_keys_aux(tmpfp, Context->hdrs[Context->v2r[i]]);
3885 extract_keys_aux(tmpfp, h);
3890 mutt_any_key_to_continue(NULL);
3893 static void crypt_fetch_signatures(BODY ***signatures, BODY * a, int *n)
3895 for (; a; a = a->next) {
3896 if (a->type == TYPEMULTIPART) {
3897 crypt_fetch_signatures(signatures, a->parts, n);
3900 p_realloc(signatures, *n + 6);
3902 (*signatures)[(*n)++] = a;
3907 int mutt_signed_handler(BODY *a, STATE *s)
3909 unsigned major, minor;
3911 int rc, i, goodsig = 1, sigcnt = 0;
3914 protocol = parameter_getval(a->parameter, "protocol");
3917 switch (mime_which_token(protocol, -1)) {
3918 case MIME_APPLICATION_PGP_SIGNATURE:
3919 major = TYPEAPPLICATION;
3920 minor = MIME_PGP_SIGNATURE;
3922 case MIME_APPLICATION_X_PKCS7_SIGNATURE:
3923 major = TYPEAPPLICATION;
3924 minor = MIME_X_PKCS7_SIGNATURE;
3926 case MIME_APPLICATION_PKCS7_SIGNATURE:
3927 major = TYPEAPPLICATION;
3928 minor = MIME_PKCS7_SIGNATURE;
3930 case MIME_MULTIPART_MIXED:
3931 major = TYPEMULTIPART;
3936 state_printf(s, _("[-- Error: "
3937 "Unknown multipart/signed protocol %s! --]\n\n"),
3939 return mutt_body_handler (a, s);
3942 /* consistency check */
3943 if (!(a && a->next && a->next->type == major &&
3944 mime_which_token(a->next->subtype, -1) == minor))
3946 state_attach_puts(_("[-- Error: "
3947 "Inconsistent multipart/signed structure! --]\n\n"),
3949 return mutt_body_handler (a, s);
3952 if (s->flags & M_DISPLAY) {
3955 crypt_fetch_signatures (&sigs, a->next, &sigcnt);
3957 FILE *tmpfp = tmpfile();
3960 mutt_error(_("Could not create temporary file"));
3962 crypt_write_signed(a, s, tmpfp);
3964 for (i = 0; i < sigcnt; i++) {
3965 if (sigs[i]->type == TYPEAPPLICATION) {
3968 switch ((subtype = mime_which_token(sigs[i]->subtype, -1))) {
3969 case MIME_PGP_SIGNATURE:
3970 case MIME_X_PKCS7_SIGNATURE:
3971 case MIME_PKCS7_SIGNATURE:
3972 if (crypt_verify_one(sigs[i], s, tmpfp, subtype != MIME_PGP_SIGNATURE) != 0)
3983 state_printf(s, _("[-- Warning: "
3984 "We can't verify %s/%s signatures. --]\n\n"),
3985 TYPE (sigs[i]), sigs[i]->subtype);
3989 b->goodsig = goodsig;
3990 b->badsig = !goodsig;
3992 /* Now display the signed body */
3993 state_attach_puts(_("[-- The following data is signed --]\n\n"), s);
3997 state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"),
4002 rc = mutt_body_handler (a, s);
4004 if (s->flags & M_DISPLAY && sigcnt)
4005 state_attach_puts (_("\n[-- End of signed data --]\n"), s);