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 bool smime_is_default = 0;
104 ** The default behaviour of Madmutt is to use PGP on all auto-sign/encryption
105 ** operations. To override and to use OpenSSL instead this must be \fIset\fP.
107 ** However, this has no effect while replying, since Madmutt will automatically
108 ** select the same application that was used to sign/encrypt the original
111 ** (Note that this variable can be overridden by unsetting $$crypt_autosmime.)
114 quadopt_t verify_sig = M_YES;
117 ** If ``\fIyes\fP'', always attempt to verify PGP or S/MIME signatures.
118 ** If ``\fIask\fP'', ask whether or not to verify the signature.
119 ** If ``\fIno\fP'', never attempt to verify cryptographic signatures.
123 string_t pgp_entry_format = m_strdup("%4n %t%f %4l/0x%k %-4a %2c %u");
126 ** This variable allows you to customize the PGP key selection menu to
127 ** your personal taste. This string is similar to ``$$index_format'', but
128 ** has its own set of \fTprintf(3)\fP-like sequences:
133 ** .dt %u .dd user id
134 ** .dt %a .dd algorithm
135 ** .dt %l .dd key length
137 ** .dt %c .dd capabilities
138 ** .dt %t .dd trust/validity of the key-uid association
139 ** .dt %[<s>] .dd date of the key where <s> is an \fTstrftime(3)\fP expression
146 /* Values used for comparing addresses. */
147 #define CRYPT_KV_VALID 1
148 #define CRYPT_KV_ADDR 2
149 #define CRYPT_KV_STRING 4
150 #define CRYPT_KV_STRONGID 8
151 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
158 /* We work based on user IDs, getting from a user ID to the key is
159 check and does not need any memory (gpgme uses reference counting). */
160 typedef struct cryptkey_t {
161 struct cryptkey_t *next;
162 int idx; /* and the user ID at this index */
163 int flags; /* global and per uid flags (for convenience) */
165 const char *uid; /* and for convenience point to this user ID */
168 DO_INIT(cryptkey_t, cryptkey);
169 static void cryptkey_wipe(cryptkey_t *key) {
170 gpgme_key_release(key->kobj);
172 DO_NEW(cryptkey_t, cryptkey);
173 DO_DELETE(cryptkey_t, cryptkey);
174 DO_SLIST(cryptkey_t, key, cryptkey_delete);
176 static cryptkey_t *cryptkey_dup(const cryptkey_t *k)
178 cryptkey_t *res = cryptkey_new();
181 gpgme_key_ref(k->kobj);
185 typedef struct crypt_entry {
190 static gpgme_key_t signature_key = NULL;
192 static void convert_to_7bit (BODY * a)
194 for (; a; a = a->next) {
195 int tok = mime_which_token(a->subtype, -1);
197 if (a->type == TYPEMULTIPART) {
198 a->encoding = ENC7BIT;
199 convert_to_7bit(a->parts);
200 } else if (a->type == TYPEMESSAGE && tok != MIME_DELIVERY_STATUS) {
201 if (a->encoding != ENC7BIT)
202 mutt_message_to_7bit(a, NULL);
203 } else if (a->encoding == ENC8BIT) {
204 a->encoding = ENCQUOTEDPRINTABLE;
205 } else if (a->encoding == ENCBINARY) {
206 a->encoding = ENCBASE64;
207 } else if (a->content && a->encoding != ENCBASE64
208 && (a->content->from || a->content->space))
210 a->encoding = ENCQUOTEDPRINTABLE;
215 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
216 FP. Convert the character set. */
217 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
221 tstr = p_dupstr(buf, len);
222 mutt_convert_string(&tstr, "utf-8", mod_cset.charset, M_ICONV_HOOK_FROM);
227 /* Return the keyID for the key K. Note that this string is valid as
228 long as K is valid */
229 static const char *crypt_keyid (cryptkey_t * k)
231 if (k->kobj && k->kobj->subkeys) {
232 const char *s = k->kobj->subkeys->keyid;
233 return m_strlen(s) == 16 ? s + 8 : s;
239 /* Return the hexstring fingerprint from the key K. */
240 static const char *crypt_fpr (cryptkey_t * k)
242 return k->kobj && k->kobj->subkeys ? k->kobj->subkeys->fpr : "";
245 /* Parse FLAGS and return a statically allocated(!) string with them. */
246 static char *crypt_key_abilities (int flags)
248 static char buff[3] = "es";
250 if (!(flags & KEYFLAG_CANENCRYPT))
252 else if (flags & KEYFLAG_PREFER_SIGNING)
255 if (!(flags & KEYFLAG_CANSIGN))
257 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
263 /* Parse FLAGS and return a character describing the most important flag. */
264 static char crypt_flags(int flags)
266 if (flags & KEYFLAG_REVOKED)
268 if (flags & KEYFLAG_EXPIRED)
270 if (flags & KEYFLAG_DISABLED)
272 if (flags & KEYFLAG_CRITICAL)
277 /* Return true whe validity of KEY is sufficient. */
278 static int crypt_id_is_strong (cryptkey_t * key)
283 if (key->flags & KEYFLAG_ISX509)
286 for (i = 0, uid = key->kobj->uids; uid; i++, uid = uid->next) {
288 return uid->validity == GPGME_VALIDITY_FULL
289 || uid->validity == GPGME_VALIDITY_ULTIMATE;
296 /* Return a bit vector describing how well the addresses ADDR and
297 U_ADDR match and whether KEY is valid. */
299 crypt_id_matches_addr(address_t *addr, address_t *u_addr, cryptkey_t *key)
303 if (!(key->flags & KEYFLAG_CANTUSE))
304 rv |= CRYPT_KV_VALID;
306 if (crypt_id_is_strong(key))
307 rv |= CRYPT_KV_STRONGID;
309 if (addr->mailbox && !m_strcasecmp(addr->mailbox, u_addr->mailbox))
312 if (addr->personal && m_strcasecmp(addr->personal, u_addr->personal))
313 rv |= CRYPT_KV_STRING;
319 /* Create a new gpgme context and return it. With FOR_SMIME set to
320 true, the protocol of the context is set to CMS. */
321 static gpgme_ctx_t create_gpgme_context(int for_smime)
326 err = gpgme_new (&ctx);
328 mutt_error(_("error creating gpgme context: %s\n"),
329 gpgme_strerror(err));
336 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
338 mutt_error(_("error enabling CMS protocol: %s\n"), gpgme_strerror(err));
345 /* Create a new gpgme data object. This is a wrapper to die on
347 static gpgme_data_t create_gpgme_data(void)
352 err = gpgme_data_new(&data);
354 mutt_error(_("error creating gpgme data object: %s\n"),
355 gpgme_strerror(err));
362 /* Create a new GPGME Data object from the mail body A. With CONVERT
363 passed as true, the lines are converted to CR,LF if required.
364 Return NULL on error or the gpgme_data_t object on success. */
365 static gpgme_data_t body_to_data_object(BODY *a, int convert)
371 if (!(fptmp = tmpfile())) {
372 mutt_perror (_("Can't create temporary file"));
376 mutt_write_mime_header(a, fptmp);
378 mutt_write_mime_body(a, fptmp);
385 data = create_gpgme_data();
387 while (fgets(buf + spare, sizeof(buf) - 1, fptmp)) {
388 int l = m_strlen(buf);
390 spare = buf[l - 1] != '\n';
391 if (!spare && (l <= 1 || buf[l - 2] != '\r')) {
395 gpgme_data_write(data, buf, l - spare);
400 gpgme_data_write(data, buf, 1);
401 gpgme_data_seek(data, 0, SEEK_SET);
406 err = gpgme_data_new_from_stream(&data, fptmp);
409 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
415 /* Create a GPGME data object from the stream FP but limit the object
416 to LENGTH bytes starting at OFFSET bytes from the beginning of the
418 static gpgme_data_t file_to_data_object(FILE *fp, long offset, long length)
423 err = gpgme_data_new_from_filepart(&data, NULL, fp, offset, length);
425 mutt_error(_("error allocating data object: %s\n"),
426 gpgme_strerror(err));
433 /* Write a GPGME data object to the stream FP. */
434 static int data_object_to_stream(gpgme_data_t data, FILE *fp)
440 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
441 ? gpgme_error_from_errno (errno) : 0);
443 mutt_error (_("error rewinding data object: %s\n"),
444 gpgme_strerror(err));
448 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
449 /* fixme: we are not really converting CRLF to LF but just
450 skipping CR. Doing it correctly needs a more complex logic */
451 for (p = buf; nread; p++, nread--) {
457 mutt_perror ("[tempfile]");
462 mutt_error(_("error reading data object: %s\n"), strerror(errno));
468 /* Copy a data object to a newly created temporay file and return that
469 filename. Caller must free. With RET_FP not NULL, don't close the
470 stream but return it there. */
471 static char *data_object_to_tempfile(gpgme_data_t data, FILE **ret_fp)
474 char tempfile[_POSIX_PATH_MAX];
478 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
480 mutt_perror (_("Can't create temporary file"));
484 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
485 ? gpgme_error_from_errno (errno) : 0);
489 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
490 if (fwrite (buf, nread, 1, fp) != 1) {
491 mutt_perror (_("Can't create temporary file"));
500 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
511 return m_strdup(tempfile);
515 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
517 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
523 if (!ctx || !r_key || !fpr)
524 return gpg_error (GPG_ERR_INV_VALUE);
526 if (strlen (fpr) < 8) /* We have at least a key ID. */
527 return gpg_error (GPG_ERR_INV_VALUE);
529 /* FIXME: We use our own context because we have to avoid the user's
530 I/O callback handlers. */
531 err = gpgme_new (&listctx);
534 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
535 err = gpgme_op_keylist_start (listctx, fpr, secret);
537 err = gpgme_op_keylist_next (listctx, r_key);
538 gpgme_release (listctx);
542 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
543 The keys must be space delimited. */
544 static gpgme_key_t *create_recipient_set(const char *s, int smime)
546 gpgme_ctx_t ctx = create_gpgme_context(smime);
547 gpgme_key_t *rset = NULL;
554 const char *p = m_strnextsp(s);
557 m_strncpy(buf, sizeof(buf), s, p - s);
558 if (p - s > 1 && p[-1] == '!') {
559 /* user wants to override the valididy of that key. */
561 buf[p - s - 1] = '\0';
562 err = gpgme_get_key2(ctx, buf, &key, 0);
564 key->uids->validity = GPGME_VALIDITY_FULL;
566 err = gpgme_get_key2(ctx, buf, &key, 0);
570 mutt_error(_("error adding recipient `%.*s': %s\n"),
571 (int)(p - s), s, gpgme_strerror(err));
576 p_realloc(&rset, rset_n + 1);
577 rset[rset_n++] = key;
582 /* NULL terminate. */
583 p_realloc(&rset, rset_n + 1);
584 rset[rset_n++] = NULL;
592 /* Make sure that the correct signer is set. Returns 0 on success. */
593 static int set_signer(gpgme_ctx_t ctx, int for_smime)
595 const char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
599 if (m_strisempty(signid))
602 err = gpgme_get_key(ctx, signid, &key, 1);
604 mutt_error(_("error getting secret key `%s': %s\n"), signid,
605 gpgme_strerror(err));
609 gpgme_signers_clear(ctx);
610 err = gpgme_signers_add(ctx, key);
611 gpgme_key_unref(key);
613 mutt_error(_("error setting secret key `%s': %s\n"), signid,
614 gpgme_strerror(err));
621 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
622 and return an allocated filename to a temporary file containing the
623 enciphered text. With USE_SMIME set to true, the smime backend is
624 used. With COMBINED_SIGNED a PGP message is signed and
625 encrypted. Returns NULL in case of error */
626 static char *encrypt_gpgme_object(gpgme_data_t plaintext, gpgme_key_t *rset,
627 int use_smime, int combined_signed)
631 gpgme_data_t ciphertext;
634 ctx = create_gpgme_context (use_smime);
636 gpgme_set_armor (ctx, 1);
638 ciphertext = create_gpgme_data ();
640 if (combined_signed) {
641 if (set_signer(ctx, use_smime)) {
642 gpgme_data_release(ciphertext);
646 err = gpgme_op_encrypt_sign(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
647 plaintext, ciphertext);
649 err = gpgme_op_encrypt(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
650 plaintext, ciphertext);
653 mutt_need_hard_redraw();
655 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
656 gpgme_data_release (ciphertext);
663 outfile = data_object_to_tempfile(ciphertext, NULL);
664 gpgme_data_release(ciphertext);
668 /* Find the "micalg" parameter from the last Gpgme operation on
669 context CTX. It is expected that this operation was a sign
670 operation. Return the algorithm name as a C string in buffer BUF
671 which must have been allocated by the caller with size BUFLEN.
672 Returns 0 on success or -1 in case of an error. The return string
673 is truncted to BUFLEN - 1. */
674 static int get_micalg(gpgme_ctx_t ctx, char *buf, ssize_t buflen)
676 gpgme_sign_result_t result = NULL;
677 const char *alg = NULL;
679 result = gpgme_op_sign_result(ctx);
680 if (result && result->signatures) {
681 alg = gpgme_hash_algo_name(result->signatures->hash_algo);
683 m_strcpy(buf, buflen, NONULL(alg));
688 static void print_time(time_t t, STATE *s)
692 setlocale(LC_TIME, "");
694 strftime(p, sizeof(p), nl_langinfo(D_T_FMT), localtime(&t));
696 strftime(p, sizeof(p), "%c", localtime(&t));
698 setlocale(LC_TIME, "C");
699 state_attach_puts(p, s);
702 /* Implementation of `sign_message'. */
704 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
705 USE_SMIME is passed as true. Returns the new body or NULL on
707 static BODY *sign_message(BODY * a, int use_smime)
714 gpgme_data_t message, signature;
716 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
718 message = body_to_data_object(a, 1);
721 signature = create_gpgme_data ();
723 ctx = create_gpgme_context (use_smime);
725 gpgme_set_armor (ctx, 1);
727 if (set_signer (ctx, use_smime)) {
728 gpgme_data_release (signature);
733 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
734 mutt_need_hard_redraw ();
735 gpgme_data_release (message);
737 gpgme_data_release (signature);
739 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
743 sigfile = data_object_to_tempfile(signature, NULL);
744 gpgme_data_release (signature);
751 t->type = TYPEMULTIPART;
752 t->subtype = m_strdup("signed");
753 t->encoding = ENC7BIT;
755 t->disposition = DISPINLINE;
757 parameter_set_boundary(&t->parameter);
758 parameter_setval(&t->parameter, "protocol",
759 use_smime ? "application/pkcs7-signature"
760 : "application/pgp-signature");
761 /* Get the micalg from gpgme. Old gpgme versions don't support this
762 for S/MIME so we assume sha-1 in this case. */
763 if (!get_micalg (ctx, buf, sizeof buf))
764 parameter_setval(&t->parameter, "micalg", buf);
766 parameter_setval(&t->parameter, "micalg", "sha1");
772 t->parts->next = body_new();
774 t->type = TYPEAPPLICATION;
776 t->subtype = m_strdup("pkcs7-signature");
777 parameter_setval(&t->parameter, "name", "smime.p7s");
778 t->encoding = ENCBASE64;
780 t->disposition = DISPATTACH;
781 t->d_filename = m_strdup("smime.p7s");
784 t->subtype = m_strdup("pgp-signature");
786 t->disposition = DISPINLINE;
787 t->encoding = ENC7BIT;
789 t->filename = sigfile;
790 t->unlink = 1; /* ok to remove this file after sending. */
795 /* Encrypt the mail body A to all keys given as space separated keyids
796 or fingerprints in KEYLIST and return the encrypted body. */
797 static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
799 char *outfile = NULL;
801 gpgme_key_t *rset = NULL;
802 gpgme_data_t plaintext;
804 rset = create_recipient_set(keylist, 0);
810 plaintext = body_to_data_object(a, 0);
816 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
817 gpgme_data_release (plaintext);
823 t->type = TYPEMULTIPART;
824 t->subtype = m_strdup("encrypted");
825 t->encoding = ENC7BIT;
827 t->disposition = DISPINLINE;
829 parameter_set_boundary(&t->parameter);
830 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
832 t->parts = body_new();
833 t->parts->type = TYPEAPPLICATION;
834 t->parts->subtype = m_strdup("pgp-encrypted");
835 t->parts->encoding = ENC7BIT;
837 t->parts->next = body_new();
838 t->parts->next->type = TYPEAPPLICATION;
839 t->parts->next->subtype = m_strdup("octet-stream");
840 t->parts->next->encoding = ENC7BIT;
841 t->parts->next->filename = outfile;
842 t->parts->next->use_disp = 1;
843 t->parts->next->disposition = DISPINLINE;
844 t->parts->next->unlink = 1; /* delete after sending the message */
845 t->parts->next->d_filename = m_strdup("msg.asc");
850 /* Encrypt the mail body A to all keys given as space separated
851 fingerprints in KEYLIST and return the S/MIME encrypted body. */
852 static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
854 char *outfile = NULL;
856 gpgme_key_t *rset = NULL;
857 gpgme_data_t plaintext;
859 rset = create_recipient_set(keylist, 1);
863 plaintext = body_to_data_object(a, 0);
869 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
870 gpgme_data_release (plaintext);
876 t->type = TYPEAPPLICATION;
877 t->subtype = m_strdup("pkcs7-mime");
878 parameter_setval(&t->parameter, "name", "smime.p7m");
879 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
880 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
882 t->disposition = DISPATTACH;
883 t->d_filename = m_strdup("smime.p7m");
884 t->filename = outfile;
885 t->unlink = 1; /*delete after sending the message */
892 /* Display the common attributes of the signature summary SUM.
893 Return 1 if there is is a severe warning.
895 static int show_sig_summary (unsigned long sum,
896 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
901 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
902 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
906 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
907 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
910 state_attach_puts (_("Warning: The key used to create the "
911 "signature expired at: "), s);
913 state_attach_puts ("\n", s);
916 state_attach_puts (_("Warning: At least one certification key "
917 "has expired\n"), s);
920 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
921 gpgme_verify_result_t result;
922 gpgme_signature_t sig;
925 result = gpgme_op_verify_result (ctx);
927 for (sig = result->signatures, i = 0; sig && (i < idx);
928 sig = sig->next, i++);
930 state_attach_puts (_("Warning: The signature expired at: "), s);
931 print_time (sig ? sig->exp_timestamp : 0, s);
932 state_attach_puts ("\n", s);
935 if ((sum & GPGME_SIGSUM_KEY_MISSING))
936 state_attach_puts (_("Can't verify due to a missing "
937 "key or certificate\n"), s);
939 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
940 state_attach_puts (_("The CRL is not available\n"), s);
944 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
945 state_attach_puts (_("Available CRL is too old\n"), s);
949 if ((sum & GPGME_SIGSUM_BAD_POLICY))
950 state_attach_puts (_("A policy requirement was not met\n"), s);
952 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
953 const char *t0 = NULL, *t1 = NULL;
954 gpgme_verify_result_t result;
955 gpgme_signature_t sig;
958 state_attach_puts (_("A system error occurred"), s);
960 /* Try to figure out some more detailed system error information. */
961 result = gpgme_op_verify_result (ctx);
962 for (sig = result->signatures, i = 0; sig && (i < idx);
963 sig = sig->next, i++);
966 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
970 state_attach_puts (": ", s);
972 state_attach_puts (t0, s);
973 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
975 state_attach_puts (",", s);
976 state_attach_puts (t1, s);
979 state_attach_puts ("\n", s);
986 static void show_fingerprint (gpgme_key_t key, STATE * state)
991 const char *prefix = _("Fingerprint: ");
996 s = key->subkeys ? key->subkeys->fpr : NULL;
999 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
1001 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
1002 buf = p_new(char, bufsize);
1003 m_strcpy(buf, bufsize, prefix);
1004 p = buf + m_strlen(buf);
1005 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
1006 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
1017 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
1020 *p++ = is_pgp ? ' ' : ':';
1021 if (is_pgp && i == 7)
1026 /* just in case print remaining odd digits */
1031 state_attach_puts (buf, state);
1035 /* Show the valididy of a key used for one signature. */
1036 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
1038 gpgme_verify_result_t result = NULL;
1039 gpgme_signature_t sig = NULL;
1040 const char *txt = NULL;
1042 result = gpgme_op_verify_result (ctx);
1044 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
1046 switch (sig ? sig->validity : 0) {
1047 case GPGME_VALIDITY_UNKNOWN:
1048 txt = _("WARNING: We have NO indication whether "
1049 "the key belongs to the person named " "as shown above\n");
1051 case GPGME_VALIDITY_UNDEFINED:
1053 case GPGME_VALIDITY_NEVER:
1054 txt = _("WARNING: The key does NOT BELONG to "
1055 "the person named as shown above\n");
1057 case GPGME_VALIDITY_MARGINAL:
1058 txt = _("WARNING: It is NOT certain that the key "
1059 "belongs to the person named as shown above\n");
1061 case GPGME_VALIDITY_FULL:
1062 case GPGME_VALIDITY_ULTIMATE:
1067 state_attach_puts (txt, s);
1070 /* Show information about one signature. This fucntion is called with
1071 the context CTX of a sucessful verification operation and the
1072 enumerator IDX which should start at 0 and incremete for each
1075 Return values are: 0 for normal procession, 1 for a bad signature,
1076 2 for a signature with a warning or -1 for no more signature. */
1077 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
1080 const char *fpr, *uid;
1081 gpgme_key_t key = NULL;
1082 int i, anybad = 0, anywarn = 0;
1084 gpgme_user_id_t uids = NULL;
1085 gpgme_verify_result_t result;
1086 gpgme_signature_t sig;
1087 gpgme_error_t err = GPG_ERR_NO_ERROR;
1089 result = gpgme_op_verify_result (ctx);
1091 /* FIXME: this code should use a static variable and remember
1092 the current position in the list of signatures, IMHO.
1095 for (i = 0, sig = result->signatures; sig && (i < idx);
1096 i++, sig = sig->next);
1098 return -1; /* Signature not found. */
1100 if (signature_key) {
1101 gpgme_key_unref(signature_key);
1102 signature_key = NULL;
1105 created = sig->timestamp;
1109 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1112 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1114 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1116 signature_key = key;
1119 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1120 error. Do it here to avoid a double free. */
1124 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1126 state_attach_puts (_("Error getting key information: "), s);
1127 state_attach_puts (gpg_strerror (err), s);
1128 state_attach_puts ("\n", s);
1131 else if ((sum & GPGME_SIGSUM_GREEN)) {
1132 state_attach_puts (_("Good signature from: "), s);
1133 state_attach_puts (uid, s);
1134 state_attach_puts ("\n", s);
1135 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1137 /* Skip primary UID. */
1141 state_attach_puts (_(" aka: "), s);
1142 state_attach_puts (uids->uid, s);
1143 state_attach_puts ("\n", s);
1145 state_attach_puts (_(" created: "), s);
1146 print_time (created, s);
1147 state_attach_puts ("\n", s);
1148 if (show_sig_summary (sum, ctx, key, idx, s))
1150 show_one_sig_validity (ctx, idx, s);
1152 else if ((sum & GPGME_SIGSUM_RED)) {
1153 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1154 state_attach_puts (uid, s);
1155 state_attach_puts ("\n", s);
1156 show_sig_summary (sum, ctx, key, idx, s);
1158 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1159 signature, so we display what a PGP user expects: The name,
1160 fingerprint and the key validity (which is neither fully or
1162 state_attach_puts (_("Good signature from: "), s);
1163 state_attach_puts (uid, s);
1164 state_attach_puts ("\n", s);
1165 state_attach_puts (_(" created: "), s);
1166 print_time (created, s);
1167 state_attach_puts ("\n", s);
1168 show_one_sig_validity (ctx, idx, s);
1169 show_fingerprint (key, s);
1170 if (show_sig_summary (sum, ctx, key, idx, s))
1173 else { /* can't decide (yellow) */
1175 state_attach_puts (_("Error checking signature"), s);
1176 state_attach_puts ("\n", s);
1177 show_sig_summary (sum, ctx, key, idx, s);
1180 if (key != signature_key)
1181 gpgme_key_unref(key);
1184 return anybad ? 1 : anywarn ? 2 : 0;
1187 /* Do the actual verification step. With IS_SMIME set to true we
1188 assume S/MIME (surprise!) */
1189 static int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
1195 gpgme_data_t signature, message;
1197 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1201 /* We need to tell gpgme about the encoding because the backend can't
1202 auto-detect plain base-64 encoding which is used by S/MIME. */
1204 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1206 err = gpgme_data_new_from_stream(&message, fp);
1208 gpgme_data_release (signature);
1209 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1212 ctx = create_gpgme_context (is_smime);
1214 /* Note: We don't need a current time output because GPGME avoids
1215 such an attack by separating the meta information from the
1217 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1219 err = gpgme_op_verify (ctx, signature, message, NULL);
1220 mutt_need_hard_redraw ();
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, COLS - SW, mod_crypt.pgp_entry_format, crypt_entry_fmt,
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)
2292 : _crypt_compare_address (a, b));
2296 /* Compare two key IDs and the addresses to be used for sorting. */
2297 static int _crypt_compare_keyid (const void *a, const void *b)
2299 cryptkey_t **s = (cryptkey_t **) a;
2300 cryptkey_t **t = (cryptkey_t **) b;
2303 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2306 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2309 static int crypt_compare_keyid (const void *a, const void *b)
2311 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2312 : _crypt_compare_keyid (a, b));
2315 /* Compare 2 creation dates and the addresses. For sorting. */
2316 static int _crypt_compare_date (const void *a, const void *b)
2318 cryptkey_t **s = (cryptkey_t **) a;
2319 cryptkey_t **t = (cryptkey_t **) b;
2320 unsigned long ts = 0, tt = 0;
2322 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2323 ts = (*s)->kobj->subkeys->timestamp;
2324 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2325 tt = (*t)->kobj->subkeys->timestamp;
2332 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2335 static int crypt_compare_date (const void *a, const void *b)
2337 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2338 : _crypt_compare_date (a, b));
2341 /* Compare two trust values, the key length, the creation dates. the
2342 addresses and the key IDs. For sorting. */
2343 static int _crypt_compare_trust (const void *a, const void *b)
2345 cryptkey_t **s = (cryptkey_t **) a;
2346 cryptkey_t **t = (cryptkey_t **) b;
2347 unsigned long ts = 0, tt = 0;
2350 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2351 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2354 if ((*s)->kobj->uids)
2355 ts = (*s)->kobj->uids->validity;
2356 if ((*t)->kobj->uids)
2357 tt = (*t)->kobj->uids->validity;
2358 if ((r = (tt - ts)))
2361 if ((*s)->kobj->subkeys)
2362 ts = (*s)->kobj->subkeys->length;
2363 if ((*t)->kobj->subkeys)
2364 tt = (*t)->kobj->subkeys->length;
2368 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2369 ts = (*s)->kobj->subkeys->timestamp;
2370 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2371 tt = (*t)->kobj->subkeys->timestamp;
2377 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2379 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2382 static int crypt_compare_trust (const void *a, const void *b)
2384 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2385 : _crypt_compare_trust (a, b));
2388 /* Print the X.500 Distinguished Name part KEY from the array of parts
2390 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2394 for (; dn->key; dn++) {
2395 if (!m_strcmp(dn->key, key)) {
2398 print_utf8 (fp, dn->value, m_strlen(dn->value));
2405 /* Print all parts of a DN in a standard sequence. */
2406 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2408 const char *stdpart[] = {
2409 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2411 int any = 0, any2 = 0, i;
2413 for (i = 0; stdpart[i]; i++) {
2416 any = print_dn_part (fp, dn, stdpart[i]);
2418 /* now print the rest without any specific ordering */
2419 for (; dn->key; dn++) {
2420 for (i = 0; stdpart[i]; i++) {
2421 if (!m_strcmp(dn->key, stdpart[i]))
2429 any = print_dn_part (fp, dn, dn->key);
2438 /* Parse an RDN; this is a helper to parse_dn(). */
2439 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2440 const unsigned char *string)
2442 const unsigned char *s, *s1;
2446 /* parse attributeType */
2447 for (s = string + 1; *s && *s != '='; s++);
2449 return NULL; /* error */
2452 return NULL; /* empty key */
2453 array->key = p_dupstr(string, n );
2454 p = (unsigned char *) array->key;
2457 if (*string == '#') { /* hexstring */
2459 for (s = string; hexval(*s) >= 0; s++)
2463 return NULL; /* empty or odd number of digits */
2465 p = p_new(unsigned char, n + 1);
2466 array->value = (char *) p;
2467 for (s1 = string; n; s1 += 2, n--)
2468 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2471 else { /* regular v3 quoted string */
2472 for (n = 0, s = string; *s; s++) {
2473 if (*s == '\\') { /* pair */
2475 if (*s == ',' || *s == '=' || *s == '+'
2476 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2477 || *s == '\\' || *s == '"' || *s == ' ')
2479 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2484 return NULL; /* invalid escape sequence */
2487 return NULL; /* invalid encoding */
2488 else if (*s == ',' || *s == '=' || *s == '+'
2489 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2495 p = p_new(unsigned char, n + 1);
2496 array->value = (char *) p;
2497 for (s = string; n; s++, n--) {
2500 if (hexval(*s) >= 0) {
2501 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2516 /* Parse a DN and return an array-ized one. This is not a validating
2517 parser and it does not support any old-stylish syntax; gpgme is
2518 expected to return only rfc2253 compatible strings. */
2519 static struct dn_array_s *parse_dn (const unsigned char *string)
2521 struct dn_array_s *array;
2522 ssize_t arrayidx, arraysize;
2525 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2526 array = p_new(struct dn_array_s, arraysize + 1);
2529 while (*string == ' ')
2533 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2534 struct dn_array_s *a2;
2537 a2 = p_new(struct dn_array_s, arraysize + 1);
2538 for (i = 0; i < arrayidx; i++) {
2539 a2[i].key = array[i].key;
2540 a2[i].value = array[i].value;
2545 array[arrayidx].key = NULL;
2546 array[arrayidx].value = NULL;
2547 string = parse_dn_part (array + arrayidx, string);
2551 while (*string == ' ')
2553 if (*string && *string != ',' && *string != ';' && *string != '+')
2554 goto failure; /* invalid delimiter */
2558 array[arrayidx].key = NULL;
2559 array[arrayidx].value = NULL;
2563 for (i = 0; i < arrayidx; i++) {
2564 p_delete(&array[i].key);
2565 p_delete(&array[i].value);
2572 /* Print a nice representation of the USERID and make sure it is
2573 displayed in a proper way, which does mean to reorder some parts
2574 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2575 functions. It is utf-8 encoded. */
2576 static void parse_and_print_user_id(FILE * fp, const char *userid)
2581 if (*userid == '<') {
2582 s = strchr (userid + 1, '>');
2584 print_utf8 (fp, userid + 1, s - userid - 1);
2586 else if (*userid == '(')
2587 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2588 else if (*userid & ~127 || __m_strdigits[(int)*userid] == 255)
2589 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2591 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2594 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2596 print_dn_parts (fp, dn);
2597 for (i = 0; dn[i].key; i++) {
2598 p_delete(&dn[i].key);
2599 p_delete(&dn[i].value);
2607 KEY_CAP_CAN_ENCRYPT,
2612 static unsigned int key_check_cap(gpgme_key_t key, key_cap_t cap)
2614 gpgme_subkey_t subkey = NULL;
2615 unsigned int ret = 0;
2618 case KEY_CAP_CAN_ENCRYPT:
2619 if (!(ret = key->can_encrypt))
2620 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2621 if ((ret = subkey->can_encrypt))
2624 case KEY_CAP_CAN_SIGN:
2625 if (!(ret = key->can_sign))
2626 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2627 if ((ret = subkey->can_sign))
2630 case KEY_CAP_CAN_CERTIFY:
2631 if (!(ret = key->can_certify))
2632 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2633 if ((ret = subkey->can_certify))
2642 /* Print verbose information about a key or certificate to FP. */
2643 static void print_key_info (gpgme_key_t key, FILE * fp)
2646 const char *s = NULL, *s2 = NULL;
2649 char shortbuf[STRING];
2650 unsigned long aval = 0;
2654 gpgme_user_id_t uid = NULL;
2657 setlocale (LC_TIME, Locale);
2659 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2661 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2666 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2669 fputs (_("[Invalid]"), fp);
2673 print_utf8 (fp, s, m_strlen(s));
2675 parse_and_print_user_id (fp, s);
2679 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2680 tt = key->subkeys->timestamp;
2682 tm = localtime (&tt);
2684 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2686 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2688 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2691 if (key->subkeys && (key->subkeys->expires > 0)) {
2692 tt = key->subkeys->expires;
2694 tm = localtime (&tt);
2696 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2698 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2700 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2704 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2708 s2 = is_pgp ? "PGP" : "X.509";
2711 aval = key->subkeys->length;
2713 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2715 fprintf (fp, _("Key Usage .: "));
2718 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2719 fprintf (fp, "%s%s", delim, _("encryption"));
2722 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2723 fprintf (fp, "%s%s", delim, _("signing"));
2726 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2727 fprintf (fp, "%s%s", delim, _("certification"));
2733 s = key->subkeys->fpr;
2734 fputs (_("Fingerprint: "), fp);
2735 if (is_pgp && m_strlen(s) == 40) {
2736 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2741 putc (is_pgp ? ' ' : ':', fp);
2742 if (is_pgp && i == 4)
2747 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2750 putc (is_pgp ? ' ' : ':', fp);
2751 if (is_pgp && i == 7)
2755 fprintf (fp, "%s\n", s);
2758 if (key->issuer_serial) {
2759 s = key->issuer_serial;
2761 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2764 if (key->issuer_name) {
2765 s = key->issuer_name;
2767 fprintf (fp, _("Issued By .: "));
2768 parse_and_print_user_id (fp, s);
2773 /* For PGP we list all subkeys. */
2775 gpgme_subkey_t subkey = NULL;
2777 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2781 if (m_strlen(s) == 16)
2782 s += 8; /* display only the short keyID */
2783 fprintf (fp, _("Subkey ....: 0x%s"), s);
2784 if (subkey->revoked) {
2786 fputs (_("[Revoked]"), fp);
2788 if (subkey->invalid) {
2790 fputs (_("[Invalid]"), fp);
2792 if (subkey->expired) {
2794 fputs (_("[Expired]"), fp);
2796 if (subkey->disabled) {
2798 fputs (_("[Disabled]"), fp);
2802 if (subkey->timestamp > 0) {
2803 tt = subkey->timestamp;
2805 tm = localtime (&tt);
2807 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2809 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2811 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2814 if (subkey->expires > 0) {
2815 tt = subkey->expires;
2817 tm = localtime (&tt);
2819 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2821 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2823 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2827 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2832 aval = subkey->length;
2836 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2838 fprintf (fp, _("Key Usage .: "));
2841 if (subkey->can_encrypt) {
2842 fprintf (fp, "%s%s", delim, _("encryption"));
2845 if (subkey->can_sign) {
2846 fprintf (fp, "%s%s", delim, _("signing"));
2849 if (subkey->can_certify) {
2850 fprintf (fp, "%s%s", delim, _("certification"));
2858 setlocale (LC_TIME, "C");
2862 /* Show detailed information about the selected key */
2863 static void verify_key (cryptkey_t * key)
2866 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2868 gpgme_ctx_t listctx = NULL;
2870 gpgme_key_t k = NULL;
2873 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2875 mutt_perror (_("Can't create temporary file"));
2878 mutt_message _("Collecting data...");
2880 print_key_info (key->kobj, fp);
2882 err = gpgme_new (&listctx);
2884 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2885 gpgme_strerror (err));
2888 if ((key->flags & KEYFLAG_ISX509))
2889 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2893 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2895 err = gpgme_op_keylist_start (listctx, s, 0);
2899 err = gpgme_op_keylist_next (listctx, &k);
2901 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2904 gpgme_op_keylist_end (listctx);
2906 print_key_info (k, fp);
2909 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2916 gpgme_release (listctx);
2918 mutt_clear_error ();
2919 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2920 mutt_pager(cmd, tempfile, 0, NULL);
2923 /* Implementation of `findkeys'. */
2925 static void add_hints(string_array *arr, const char *s)
2931 int l = strcspn(s, " ,.:\"()<>\n");
2932 string_array_append(arr, p_dupstr(s, l));
2934 s += strspn(s, " ,.:\"()<>\n");
2938 /* Return a list of keys which are candidates for the selection. */
2940 get_candidates(string_array *hints, unsigned int app, int secret)
2942 cryptkey_t *res = NULL, **kend = &res;
2947 if (hints->len <= 0)
2949 string_array_append(hints, NULL);
2950 ctx = create_gpgme_context(0);
2952 if ((app & APPLICATION_PGP)) {
2953 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2956 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2957 gpgme_strerror(err));
2962 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2963 gpgme_user_id_t uid = NULL;
2964 unsigned int flags = 0;
2967 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2968 flags |= KEYFLAG_CANENCRYPT;
2969 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2970 flags |= KEYFLAG_CANSIGN;
2972 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2973 cryptkey_t *k = p_new(cryptkey_t, 1);
2982 if (gpg_err_code(err) != GPG_ERR_EOF)
2983 mutt_error(_("gpgme_op_keylist_next failed: %s"), gpgme_strerror(err));
2984 gpgme_op_keylist_end(ctx);
2987 if ((app & APPLICATION_SMIME)) {
2988 gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
2989 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2992 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2993 gpgme_strerror(err));
2998 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2999 gpgme_user_id_t uid = NULL;
3000 unsigned int flags = KEYFLAG_ISX509;
3003 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
3004 flags |= KEYFLAG_CANENCRYPT;
3005 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
3006 flags |= KEYFLAG_CANSIGN;
3008 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3009 cryptkey_t *k = p_new(cryptkey_t, 1);
3018 if (gpg_err_code(err) != GPG_ERR_EOF)
3019 mutt_error(_("gpgme_op_keylist_next failed: %s"),
3020 gpgme_strerror(err));
3021 gpgme_op_keylist_end(ctx);
3028 /* Display a menu to select a key from the array KEYS. FORCED_VALID
3029 will be set to true on return if the user did override the the
3031 static cryptkey_t *crypt_select_key (cryptkey_t * keys,
3032 address_t * p, const char *s,
3033 unsigned int app, int *forced_valid)
3036 cryptkey_t **cryptkey_table;
3039 char helpstr[STRING], buf[LONG_STRING];
3041 int (*f) (const void *, const void *);
3042 int menu_to_use = 0;
3047 /* build the key table */
3049 cryptkey_table = NULL;
3050 for (k = keys; k; k = k->next) {
3051 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
3058 p_realloc(&cryptkey_table, keymax);
3061 cryptkey_table[i++] = k;
3064 if (!i && unusable) {
3065 mutt_error _("All matching keys are marked expired/revoked.");
3071 switch (PgpSortKeys & SORT_MASK) {
3073 f = crypt_compare_date;
3076 f = crypt_compare_keyid;
3079 f = crypt_compare_address;
3083 f = crypt_compare_trust;
3086 qsort (cryptkey_table, i, sizeof (cryptkey_t *), f);
3088 if (app & APPLICATION_PGP)
3089 menu_to_use = MENU_KEY_SELECT_PGP;
3090 else if (app & APPLICATION_SMIME)
3091 menu_to_use = MENU_KEY_SELECT_SMIME;
3094 mutt_make_help (buf, sizeof (buf), _("Exit "), menu_to_use, OP_EXIT);
3095 m_strcat(helpstr, sizeof(helpstr), buf);
3096 mutt_make_help (buf, sizeof (buf), _("Select "), menu_to_use,
3097 OP_GENERIC_SELECT_ENTRY);
3098 m_strcat(helpstr, sizeof(helpstr), buf);
3099 mutt_make_help (buf, sizeof (buf), _("Check key "),
3100 menu_to_use, OP_VERIFY_KEY);
3101 m_strcat(helpstr, sizeof(helpstr), buf);
3102 mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
3103 m_strcat(helpstr, sizeof(helpstr), buf);
3105 menu = mutt_new_menu ();
3107 menu->make_entry = crypt_entry;
3108 menu->menu = menu_to_use;
3109 menu->help = helpstr;
3110 menu->data = cryptkey_table;
3115 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3116 ts = _("PGP and S/MIME keys matching");
3117 else if ((app & APPLICATION_PGP))
3118 ts = _("PGP keys matching");
3119 else if ((app & APPLICATION_SMIME))
3120 ts = _("S/MIME keys matching");
3122 ts = _("keys matching");
3125 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3127 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3131 mutt_clear_error ();
3135 switch (mutt_menuLoop (menu)) {
3137 verify_key (cryptkey_table[menu->current]);
3138 menu->redraw = REDRAW_FULL;
3142 mutt_message ("%s", cryptkey_table[menu->current]->uid);
3145 case OP_GENERIC_SELECT_ENTRY:
3146 /* FIXME make error reporting more verbose - this should be
3147 easy because gpgme provides more information */
3148 if (option (OPTPGPCHECKTRUST)) {
3149 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE ) {
3150 mutt_error(_("This key can't be used: "
3151 "expired/disabled/revoked."));
3156 if (option (OPTPGPCHECKTRUST) &&
3157 ((cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3158 || !crypt_id_is_strong (cryptkey_table[menu->current]))) {
3160 char buff[LONG_STRING];
3162 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3163 s = N_("ID is expired/disabled/revoked.");
3165 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3166 gpgme_user_id_t uid = NULL;
3171 uid = cryptkey_table[menu->current]->kobj->uids;
3172 for (j = 0; (j < cryptkey_table[menu->current]->idx) && uid;
3173 j++, uid = uid->next);
3175 val = uid->validity;
3178 case GPGME_VALIDITY_UNKNOWN:
3179 case GPGME_VALIDITY_UNDEFINED:
3180 warn_s = N_("ID has undefined validity.");
3182 case GPGME_VALIDITY_NEVER:
3183 warn_s = N_("ID is not valid.");
3185 case GPGME_VALIDITY_MARGINAL:
3186 warn_s = N_("ID is only marginally valid.");
3188 case GPGME_VALIDITY_FULL:
3189 case GPGME_VALIDITY_ULTIMATE:
3193 snprintf (buff, sizeof (buff),
3194 _("%s Do you really want to use the key?"), _(warn_s));
3196 if (mutt_yesorno (buff, 0) != 1) {
3197 mutt_clear_error ();
3204 k = cryptkey_dup(cryptkey_table[menu->current]);
3215 mutt_menuDestroy (&menu);
3216 p_delete(&cryptkey_table);
3218 set_option (OPTNEEDREDRAW);
3224 crypt_getkeybyaddr(address_t * a, int abilities, int app, int *forced_valid)
3231 int this_key_has_strong;
3232 int this_key_has_weak;
3233 int this_key_has_invalid;
3236 cryptkey_t *keys, *k;
3237 cryptkey_t *the_valid_key = NULL;
3238 cryptkey_t *matches = NULL;
3239 cryptkey_t **matches_endp = &matches;
3245 string_array_init(&hints);
3246 add_hints(&hints, a->mailbox);
3247 add_hints(&hints, a->personal);
3249 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3250 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3251 string_array_wipe(&hints);
3257 for (k = keys; k; k = k->next) {
3258 if (abilities && !(k->flags & abilities)) {
3262 this_key_has_weak = 0; /* weak but valid match */
3263 this_key_has_invalid = 0; /* invalid match */
3264 this_key_has_strong = 0; /* strong and valid match */
3265 match = 0; /* any match */
3267 r = rfc822_parse_adrlist (NULL, k->uid);
3268 for (p = r; p; p = p->next) {
3269 int validity = crypt_id_matches_addr (a, p, k);
3271 if (validity & CRYPT_KV_MATCH) /* something matches */
3274 /* is this key a strong candidate? */
3275 if ((validity & CRYPT_KV_VALID)
3276 && (validity & CRYPT_KV_STRONGID)
3277 && (validity & CRYPT_KV_ADDR)) {
3278 if (the_valid_key && the_valid_key != k)
3281 this_key_has_strong = 1;
3283 else if ((validity & CRYPT_KV_MATCH)
3284 && !(validity & CRYPT_KV_VALID))
3285 this_key_has_invalid = 1;
3286 else if ((validity & CRYPT_KV_MATCH)
3287 && (!(validity & CRYPT_KV_STRONGID)
3288 || !(validity & CRYPT_KV_ADDR)))
3289 this_key_has_weak = 1;
3291 address_list_wipe(&r);
3296 if (!this_key_has_strong && this_key_has_invalid)
3298 if (!this_key_has_strong && this_key_has_weak)
3301 *matches_endp = tmp = cryptkey_dup(k);
3302 matches_endp = &tmp->next;
3303 the_valid_key = tmp;
3306 key_list_wipe(&keys);
3309 if (the_valid_key && !multi && !weak
3310 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3312 * There was precisely one strong match on a valid ID, there
3313 * were no valid keys with weak matches, and we aren't
3314 * interested in seeing invalid keys.
3316 * Proceed without asking the user.
3318 k = cryptkey_dup(the_valid_key);
3321 * Else: Ask the user.
3323 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3325 key_list_wipe(&matches);
3335 crypt_getkeybystr(const char *p, int abilities, int app, int *forced_valid)
3338 cryptkey_t *matches = NULL;
3339 cryptkey_t **matches_endp = &matches;
3343 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3349 string_array_init(&hints);
3350 add_hints(&hints, p);
3351 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3352 string_array_wipe(&hints);
3358 for (k = keys; k; k = k->next) {
3359 const char *s = crypt_keyid(k);
3361 if (abilities && !(k->flags & abilities))
3366 if (!*p || !m_strcasecmp(p, s)
3367 || (!m_strncasecmp(p, "0x", 2) && !m_strcasecmp(p + 2, s))
3368 || m_stristr(k->uid, p))
3372 *matches_endp = tmp = cryptkey_dup(k);
3373 matches_endp = &tmp->next;
3376 key_list_wipe(&keys);
3379 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3380 key_list_wipe(&matches);
3387 /* Display TAG as a prompt to ask for a key.
3388 * ABILITIES describe the required key abilities (sign, encrypt) and APP the
3389 * type of the requested key; ether S/MIME or PGP.
3390 * Return a copy of the key or NULL if not found. */
3392 crypt_ask_for_key(const char *tag, int abilities, int app, int *forced_valid)
3399 forced_valid = &dummy;
3405 if (mutt_get_field(tag, resp, sizeof(resp), M_CLEAR) != 0)
3408 if (m_strisempty(resp))
3411 if ((key = crypt_getkeybystr(resp, abilities, app, forced_valid)))
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 = mutt_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);
3459 /* check for e-mail address */
3460 if (strchr(keyID, '@') && (a = rfc822_parse_adrlist(NULL, keyID))) {
3461 rfc822_qualify(a, mutt_fqdn(1));
3462 address_list_wipe(&addr);
3465 key = crypt_getkeybystr(keyID, KEYFLAG_CANENCRYPT, app,
3472 key = crypt_getkeybyaddr(addr, KEYFLAG_CANENCRYPT, app, &forced_valid);
3475 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), addr->mailbox);
3476 key = crypt_ask_for_key(buf, KEYFLAG_CANENCRYPT, app,
3479 address_list_wipe(&lst);
3480 address_list_wipe(&addr);
3481 buffer_delete(&keylist);
3487 buffer_addch(keylist, ' ');
3488 buffer_addstr(keylist, "0x");
3489 buffer_addstr(keylist, crypt_fpr(key));
3491 buffer_addch(keylist, '!');
3493 key_list_wipe(&key);
3494 address_list_wipe(&addr);
3497 address_list_wipe(&lst);
3498 return buffer_unwrap(&keylist);
3501 int crypt_get_keys(HEADER *msg, char **keylist)
3503 /* Do a quick check to make sure that we can find all of the encryption
3504 * keys if the user has requested this service.
3509 if (msg->security & ENCRYPT) {
3510 if (msg->security & APPLICATION_PGP) {
3511 set_option(OPTPGPCHECKTRUST);
3512 *keylist = find_keys(msg->env, APPLICATION_PGP);
3513 unset_option(OPTPGPCHECKTRUST);
3518 if (msg->security & APPLICATION_SMIME) {
3519 *keylist = find_keys(msg->env, APPLICATION_SMIME);
3529 int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
3535 if (msg->security & APPLICATION_SMIME)
3537 if (msg->security & APPLICATION_PGP)
3541 ? mutt_multi_choice(_("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3543 : mutt_multi_choice(_("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3547 case 1: /* (e)ncrypt */
3548 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3549 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3552 case 2: /* (s)ign */
3553 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3554 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3557 case 3: /* sign (a)s */
3558 p = crypt_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN,
3559 is_smime ? APPLICATION_SMIME : APPLICATION_PGP,
3562 snprintf(buf, sizeof(buf), "0x%s", crypt_keyid(p));
3563 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs, buf);
3565 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3567 *redraw = REDRAW_FULL;
3570 case 4: /* (b)oth */
3572 msg->security = SMIMEENCRYPT | SMIMESIGN;
3574 msg->security = PGPENCRYPT | PGPSIGN;
3578 case 5: /* (p)gp or s/(m)ime */
3579 is_smime = !is_smime;
3582 case 6: /* (c)lear */
3583 return msg->security = 0;
3587 msg->security &= ~APPLICATION_PGP;
3588 msg->security |= APPLICATION_SMIME;
3590 msg->security &= ~APPLICATION_SMIME;
3591 msg->security |= APPLICATION_PGP;
3594 return msg->security;
3597 int crypt_smime_verify_sender(HEADER *h)
3599 address_t *sender = NULL;
3600 unsigned int ret = 1;
3603 h->env->from = mutt_expand_aliases(h->env->from);
3604 sender = h->env->from;
3605 } else if (h->env->sender) {
3606 h->env->sender = mutt_expand_aliases (h->env->sender);
3607 sender = h->env->sender;
3611 mutt_any_key_to_continue ("Failed to figure out sender");
3615 if (signature_key) {
3616 gpgme_key_t key = signature_key;
3617 gpgme_user_id_t uid = NULL;
3618 int sender_length = 0;
3621 sender_length = m_strlen(sender->mailbox);
3622 for (uid = key->uids; uid && ret; uid = uid->next) {
3623 uid_length = m_strlen(uid->email);
3624 if (1 && (uid->email[0] == '<')
3625 && (uid->email[uid_length - 1] == '>')
3626 && (uid_length == sender_length + 2)
3627 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3631 mutt_any_key_to_continue ("Failed to verify sender");
3635 if (signature_key) {
3636 gpgme_key_unref(signature_key);
3637 signature_key = NULL;
3642 static void crypt_invoke_import(FILE *stream, int smime)
3644 gpgme_ctx_t ctx = create_gpgme_context(smime);
3648 err = gpgme_data_new_from_stream(&data, stream);
3650 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3655 err = gpgme_op_import(ctx, data);
3657 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3658 gpgme_data_release(data);
3663 gpgme_data_release(data);
3668 static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
3671 FILE *tmpfp = tmpfile();
3673 if (tmpfp == NULL) {
3674 mutt_perror (_("Can't create temporary file"));
3681 mutt_body_handler(top, &s);
3684 crypt_invoke_import(tmpfp, 0);
3688 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3692 for (; top; top = top->next) {
3693 if (!tag || top->tagged)
3694 pgp_extract_keys_from_attachment (fp, top);
3701 void crypt_invoke_message (int type)
3703 if (type & APPLICATION_PGP) {
3704 mutt_message _("Invoking PGP...");
3706 else if (type & APPLICATION_SMIME) {
3707 mutt_message _("Invoking S/MIME...");
3711 int mutt_protect (HEADER * msg, char *keylist)
3713 BODY *pbody = NULL, *tmp_pbody = NULL;
3714 BODY *tmp_smime_pbody = NULL;
3715 BODY *tmp_pgp_pbody = NULL;
3716 int flags = msg->security;
3721 tmp_smime_pbody = msg->content;
3722 tmp_pgp_pbody = msg->content;
3724 if (msg->security & SIGN) {
3725 if (msg->security & APPLICATION_SMIME) {
3726 if (!(tmp_pbody = sign_message(msg->content, 1)))
3728 pbody = tmp_smime_pbody = tmp_pbody;
3731 if ((msg->security & APPLICATION_PGP)
3732 && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
3733 if (!(tmp_pbody = sign_message(msg->content, 0)))
3737 pbody = tmp_pgp_pbody = tmp_pbody;
3740 if ((msg->security & APPLICATION_SMIME)
3741 && (msg->security & APPLICATION_PGP)) {
3742 /* here comes the draft ;-) */
3747 if (msg->security & ENCRYPT) {
3748 if ((msg->security & APPLICATION_SMIME)) {
3749 if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
3751 /* signed ? free it! */
3754 /* free tmp_body if messages was signed AND encrypted ... */
3755 if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
3756 /* detatch and dont't delete msg->content,
3757 which tmp_smime_pbody->parts after signing. */
3758 tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
3759 msg->content->next = NULL;
3760 body_list_wipe(&tmp_smime_pbody);
3765 if ((msg->security & APPLICATION_PGP)) {
3766 if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
3769 /* did we perform a retainable signature? */
3770 if (flags != msg->security) {
3771 /* remove the outer multipart layer */
3772 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3773 /* get rid of the signature */
3774 body_list_wipe(&tmp_pgp_pbody->next);
3780 /* destroy temporary signature envelope when doing retainable
3784 if (flags != msg->security) {
3785 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3786 body_list_wipe(&tmp_pgp_pbody->next);
3792 msg->content = pbody;
3798 int crypt_query (BODY * m)
3805 if (m->type == TYPEAPPLICATION) {
3806 t |= mutt_is_application_pgp (m);
3808 t |= mutt_is_application_smime (m);
3809 if (t && m->goodsig)
3814 else if (m->type == TYPETEXT) {
3815 t |= mutt_is_application_pgp (m);
3816 if (t && m->goodsig)
3820 if (m->type == TYPEMULTIPART) {
3821 t |= mutt_is_multipart_encrypted (m);
3822 t |= mutt_is_multipart_signed (m);
3824 if (t && m->goodsig)
3828 if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
3832 u = m->parts ? ~0 : 0; /* Bits set in all parts */
3833 w = 0; /* Bits set in any part */
3835 for (p = m->parts; p; p = p->next) {
3836 v = crypt_query (p);
3840 t |= u | (w & ~GOODSIGN);
3842 if ((w & GOODSIGN) && !(u & GOODSIGN))
3850 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
3856 fseeko (s->fpin, a->hdr_offset, 0);
3857 bytes = a->length + a->offset - a->hdr_offset;
3860 if ((c = fgetc (s->fpin)) == EOF)
3868 if (c == '\n' && !hadcr)
3877 static void extract_keys_aux(FILE *fpout, HEADER *h)
3879 mutt_parse_mime_message (Context, h);
3882 if (h->security & APPLICATION_PGP) {
3883 mutt_copy_message(fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
3886 mutt_endwin (_("Trying to extract PGP keys...\n"));
3889 if (h->security & APPLICATION_SMIME) {
3890 if (h->security & ENCRYPT)
3891 mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
3892 | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
3894 mutt_copy_message(fpout, Context, h, 0, 0);
3897 mutt_message (_("Trying to extract S/MIME certificates...\n"));
3901 crypt_invoke_import(fpout, h->security & APPLICATION_SMIME);
3904 void crypt_extract_keys_from_messages(HEADER * h)
3906 FILE *tmpfp = tmpfile();
3908 mutt_error(_("Could not create temporary file"));
3914 for (i = 0; i < Context->vcount; i++) {
3915 if (!Context->hdrs[Context->v2r[i]]->tagged)
3917 extract_keys_aux(tmpfp, Context->hdrs[Context->v2r[i]]);
3920 extract_keys_aux(tmpfp, h);
3925 mutt_any_key_to_continue(NULL);
3928 static void crypt_fetch_signatures(BODY ***signatures, BODY * a, int *n)
3930 for (; a; a = a->next) {
3931 if (a->type == TYPEMULTIPART) {
3932 crypt_fetch_signatures(signatures, a->parts, n);
3935 p_realloc(signatures, *n + 6);
3937 (*signatures)[(*n)++] = a;
3942 int mutt_signed_handler(BODY *a, STATE *s)
3944 unsigned major, minor;
3946 int rc, i, goodsig = 1, sigcnt = 0;
3949 protocol = parameter_getval(a->parameter, "protocol");
3952 switch (mime_which_token(protocol, -1)) {
3953 case MIME_APPLICATION_PGP_SIGNATURE:
3954 major = TYPEAPPLICATION;
3955 minor = MIME_PGP_SIGNATURE;
3957 case MIME_APPLICATION_X_PKCS7_SIGNATURE:
3958 major = TYPEAPPLICATION;
3959 minor = MIME_X_PKCS7_SIGNATURE;
3961 case MIME_APPLICATION_PKCS7_SIGNATURE:
3962 major = TYPEAPPLICATION;
3963 minor = MIME_PKCS7_SIGNATURE;
3965 case MIME_MULTIPART_MIXED:
3966 major = TYPEMULTIPART;
3971 state_printf(s, _("[-- Error: "
3972 "Unknown multipart/signed protocol %s! --]\n\n"),
3974 return mutt_body_handler (a, s);
3977 /* consistency check */
3978 if (!(a && a->next && a->next->type == major &&
3979 mime_which_token(a->next->subtype, -1) == minor))
3981 state_attach_puts(_("[-- Error: "
3982 "Inconsistent multipart/signed structure! --]\n\n"),
3984 return mutt_body_handler (a, s);
3987 if (s->flags & M_DISPLAY) {
3990 crypt_fetch_signatures (&sigs, a->next, &sigcnt);
3992 FILE *tmpfp = tmpfile();
3995 mutt_error(_("Could not create temporary file"));
3997 crypt_write_signed(a, s, tmpfp);
3999 for (i = 0; i < sigcnt; i++) {
4000 if (sigs[i]->type == TYPEAPPLICATION) {
4003 switch ((subtype = mime_which_token(sigs[i]->subtype, -1))) {
4004 case MIME_PGP_SIGNATURE:
4005 case MIME_X_PKCS7_SIGNATURE:
4006 case MIME_PKCS7_SIGNATURE:
4007 if (crypt_verify_one(sigs[i], s, tmpfp, subtype != MIME_PGP_SIGNATURE) != 0)
4018 state_printf(s, _("[-- Warning: "
4019 "We can't verify %s/%s signatures. --]\n\n"),
4020 TYPE (sigs[i]), sigs[i]->subtype);
4024 b->goodsig = goodsig;
4025 b->badsig = !goodsig;
4027 /* Now display the signed body */
4028 state_attach_puts(_("[-- The following data is signed --]\n\n"), s);
4032 state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"),
4037 rc = mutt_body_handler (a, s);
4039 if (s->flags & M_DISPLAY && sigcnt)
4040 state_attach_puts (_("\n[-- End of signed data --]\n"), s);