2 * Copyright notice from original mutt:
3 * crypt-gpgme.c - GPGME based crypto operations
4 * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
5 * Copyright (C) 1998,1999,2000 Thomas Roessler <roessler@guug.de>
6 * Copyright (C) 2001 Thomas Roessler <roessler@guug.de>
7 * Oliver Ehli <elmy@acm.org>
8 * Copyright (C) 2002, 2003, 2004 g10 Code GmbH
11 * Copyright © 2006 Pierre Habouzit
14 #include <lib-lib/lib-lib.h>
18 #include <lib-mime/mime.h>
19 #include <lib-ui/lib-ui.h>
20 #include <lib-ui/menu.h>
21 #include <lib-mx/mx.h>
28 #include "recvattach.h"
31 @import "lib-lua/base.cpkg"
37 ** This variable controls whether or not Madmutt may automatically enable
38 ** S/MIME encryption/signing for messages. See also ``$$crypt_autoencrypt'',
39 ** ``$$crypt_replyencrypt'',
40 ** ``$$crypt_autosign'', ``$$crypt_replysign'' and ``$$smime_is_default''.
45 ** This variable controls whether or not Madmutt may automatically enable
46 ** PGP encryption/signing for messages. See also ``$$crypt_autoencrypt'',
47 ** ``$$crypt_replyencrypt'',
48 ** ``$$crypt_autosign'', ``$$crypt_replysign'' and ``$$smime_is_default''.
53 ** Setting this variable will cause Madmutt to always attempt to
54 ** cryptographically sign outgoing messages. This can be overridden
55 ** by use of the \fIpgp-menu\fP, when signing is not required or
56 ** encryption is requested as well. If ``$$smime_is_default'' is \fIset\fP,
57 ** then OpenSSL is used instead to create S/MIME messages and settings can
58 ** be overridden by use of the \fIsmime-menu\fP.
64 ** Setting this variable will cause Madmutt to always attempt to PGP
65 ** encrypt outgoing messages. This is probably only useful in
66 ** connection to the \fIsend-hook\fP command. It can be overridden
67 ** by use of the \fIpgp-menu\fP, when encryption is not required or
68 ** signing is requested as well. If ``$$smime_is_default'' is \fIset\fP,
69 ** then OpenSSL is used instead to create S/MIME messages and
70 ** settings can be overridden by use of the \fIsmime-menu\fP.
73 bool replyencrypt = 1;
76 ** If \fIset\fP, automatically PGP or OpenSSL encrypt replies to messages which are
83 ** If \fIset\fP, automatically PGP or OpenSSL sign replies to messages which are
86 ** \fBNote:\fP this does not work on messages that are encrypted \fBand\fP signed!
89 bool replysignencrypted = 1;
92 ** If \fIset\fP, automatically PGP or OpenSSL sign replies to messages
93 ** which are encrypted. This makes sense in combination with
94 ** ``$$crypt_replyencrypt'', because it allows you to sign all
95 ** messages which are automatically encrypted. This works around
96 ** the problem noted in ``$$crypt_replysign'', that Madmutt is not able
97 ** to find out whether an encrypted message is also signed.
100 bool smime_is_default = 0;
103 ** The default behaviour of Madmutt is to use PGP on all auto-sign/encryption
104 ** operations. To override and to use OpenSSL instead this must be \fIset\fP.
106 ** However, this has no effect while replying, since Madmutt will automatically
107 ** select the same application that was used to sign/encrypt the original
110 ** (Note that this variable can be overridden by unsetting $$crypt_autosmime.)
113 quadopt_t verify_sig = M_YES;
116 ** If ``\fIyes\fP'', always attempt to verify PGP or S/MIME signatures.
117 ** If ``\fIask\fP'', ask whether or not to verify the signature.
118 ** If ``\fIno\fP'', never attempt to verify cryptographic signatures.
122 string_t pgp_entry_format = m_strdup("%4n %t%f %4l/0x%k %-4a %2c %u");
125 ** This variable allows you to customize the PGP key selection menu to
126 ** your personal taste. This string is similar to ``$$index_format'', but
127 ** has its own set of \fTprintf(3)\fP-like sequences:
132 ** .dt %u .dd user id
133 ** .dt %a .dd algorithm
134 ** .dt %l .dd key length
136 ** .dt %c .dd capabilities
137 ** .dt %t .dd trust/validity of the key-uid association
138 ** .dt %[<s>] .dd date of the key where <s> is an \fTstrftime(3)\fP expression
145 /* Values used for comparing addresses. */
146 #define CRYPT_KV_VALID 1
147 #define CRYPT_KV_ADDR 2
148 #define CRYPT_KV_STRING 4
149 #define CRYPT_KV_STRONGID 8
150 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
157 /* We work based on user IDs, getting from a user ID to the key is
158 check and does not need any memory (gpgme uses reference counting). */
159 typedef struct cryptkey_t {
160 struct cryptkey_t *next;
161 int idx; /* and the user ID at this index */
162 int flags; /* global and per uid flags (for convenience) */
164 const char *uid; /* and for convenience point to this user ID */
167 DO_INIT(cryptkey_t, cryptkey);
168 static void cryptkey_wipe(cryptkey_t *key) {
169 gpgme_key_release(key->kobj);
171 DO_NEW(cryptkey_t, cryptkey);
172 DO_DELETE(cryptkey_t, cryptkey);
173 DO_SLIST(cryptkey_t, key, cryptkey_delete);
175 static cryptkey_t *cryptkey_dup(const cryptkey_t *k)
177 cryptkey_t *res = cryptkey_new();
180 gpgme_key_ref(k->kobj);
184 typedef struct crypt_entry {
189 static gpgme_key_t signature_key = NULL;
191 static void convert_to_7bit (BODY * a)
193 for (; a; a = a->next) {
194 int tok = mime_which_token(a->subtype, -1);
196 if (a->type == TYPEMULTIPART) {
197 a->encoding = ENC7BIT;
198 convert_to_7bit(a->parts);
199 } else if (a->type == TYPEMESSAGE && tok != MIME_DELIVERY_STATUS) {
200 if (a->encoding != ENC7BIT)
201 mutt_message_to_7bit(a, NULL);
202 } else if (a->encoding == ENC8BIT) {
203 a->encoding = ENCQUOTEDPRINTABLE;
204 } else if (a->encoding == ENCBINARY) {
205 a->encoding = ENCBASE64;
206 } else if (a->content && a->encoding != ENCBASE64
207 && (a->content->from || a->content->space))
209 a->encoding = ENCQUOTEDPRINTABLE;
214 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
215 FP. Convert the character set. */
216 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
220 tstr = p_dupstr(buf, len);
221 mutt_convert_string(&tstr, "utf-8", mod_cset.charset, M_ICONV_HOOK_FROM);
226 /* Return the keyID for the key K. Note that this string is valid as
227 long as K is valid */
228 static const char *crypt_keyid (cryptkey_t * k)
230 if (k->kobj && k->kobj->subkeys) {
231 const char *s = k->kobj->subkeys->keyid;
232 return m_strlen(s) == 16 ? s + 8 : s;
238 /* Return the hexstring fingerprint from the key K. */
239 static const char *crypt_fpr (cryptkey_t * k)
241 return k->kobj && k->kobj->subkeys ? k->kobj->subkeys->fpr : "";
244 /* Parse FLAGS and return a statically allocated(!) string with them. */
245 static char *crypt_key_abilities (int flags)
247 static char buff[3] = "es";
249 if (!(flags & KEYFLAG_CANENCRYPT))
251 else if (flags & KEYFLAG_PREFER_SIGNING)
254 if (!(flags & KEYFLAG_CANSIGN))
256 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
262 /* Parse FLAGS and return a character describing the most important flag. */
263 static char crypt_flags(int flags)
265 if (flags & KEYFLAG_REVOKED)
267 if (flags & KEYFLAG_EXPIRED)
269 if (flags & KEYFLAG_DISABLED)
271 if (flags & KEYFLAG_CRITICAL)
276 /* Return true whe validity of KEY is sufficient. */
277 static int crypt_id_is_strong (cryptkey_t * key)
282 if (key->flags & KEYFLAG_ISX509)
285 for (i = 0, uid = key->kobj->uids; uid; i++, uid = uid->next) {
287 return uid->validity == GPGME_VALIDITY_FULL
288 || uid->validity == GPGME_VALIDITY_ULTIMATE;
295 /* Return a bit vector describing how well the addresses ADDR and
296 U_ADDR match and whether KEY is valid. */
298 crypt_id_matches_addr(address_t *addr, address_t *u_addr, cryptkey_t *key)
302 if (!(key->flags & KEYFLAG_CANTUSE))
303 rv |= CRYPT_KV_VALID;
305 if (crypt_id_is_strong(key))
306 rv |= CRYPT_KV_STRONGID;
308 if (addr->mailbox && !m_strcasecmp(addr->mailbox, u_addr->mailbox))
311 if (addr->personal && m_strcasecmp(addr->personal, u_addr->personal))
312 rv |= CRYPT_KV_STRING;
318 /* Create a new gpgme context and return it. With FOR_SMIME set to
319 true, the protocol of the context is set to CMS. */
320 static gpgme_ctx_t create_gpgme_context(int for_smime)
325 err = gpgme_new (&ctx);
327 mutt_error(_("error creating gpgme context: %s\n"),
328 gpgme_strerror(err));
335 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
337 mutt_error(_("error enabling CMS protocol: %s\n"), gpgme_strerror(err));
344 /* Create a new gpgme data object. This is a wrapper to die on
346 static gpgme_data_t create_gpgme_data(void)
351 err = gpgme_data_new(&data);
353 mutt_error(_("error creating gpgme data object: %s\n"),
354 gpgme_strerror(err));
361 /* Create a new GPGME Data object from the mail body A. With CONVERT
362 passed as true, the lines are converted to CR,LF if required.
363 Return NULL on error or the gpgme_data_t object on success. */
364 static gpgme_data_t body_to_data_object(BODY *a, int convert)
370 if (!(fptmp = tmpfile())) {
371 mutt_perror (_("Can't create temporary file"));
375 mutt_write_mime_header(a, fptmp);
377 mutt_write_mime_body(a, fptmp);
384 data = create_gpgme_data();
386 while (fgets(buf + spare, sizeof(buf) - 1, fptmp)) {
387 int l = m_strlen(buf);
389 spare = buf[l - 1] != '\n';
390 if (!spare && (l <= 1 || buf[l - 2] != '\r')) {
394 gpgme_data_write(data, buf, l - spare);
399 gpgme_data_write(data, buf, 1);
400 gpgme_data_seek(data, 0, SEEK_SET);
405 err = gpgme_data_new_from_stream(&data, fptmp);
408 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
414 /* Create a GPGME data object from the stream FP but limit the object
415 to LENGTH bytes starting at OFFSET bytes from the beginning of the
417 static gpgme_data_t file_to_data_object(FILE *fp, long offset, long length)
422 err = gpgme_data_new_from_filepart(&data, NULL, fp, offset, length);
424 mutt_error(_("error allocating data object: %s\n"),
425 gpgme_strerror(err));
432 /* Write a GPGME data object to the stream FP. */
433 static int data_object_to_stream(gpgme_data_t data, FILE *fp)
439 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
440 ? gpgme_error_from_errno (errno) : 0);
442 mutt_error (_("error rewinding data object: %s\n"),
443 gpgme_strerror(err));
447 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
448 /* fixme: we are not really converting CRLF to LF but just
449 skipping CR. Doing it correctly needs a more complex logic */
450 for (p = buf; nread; p++, nread--) {
456 mutt_perror ("[tempfile]");
461 mutt_error(_("error reading data object: %s\n"), strerror(errno));
467 /* Copy a data object to a newly created temporay file and return that
468 filename. Caller must free. With RET_FP not NULL, don't close the
469 stream but return it there. */
470 static char *data_object_to_tempfile(gpgme_data_t data, FILE **ret_fp)
473 char tempfile[_POSIX_PATH_MAX];
477 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
479 mutt_perror (_("Can't create temporary file"));
483 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
484 ? gpgme_error_from_errno (errno) : 0);
488 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
489 if (fwrite (buf, nread, 1, fp) != 1) {
490 mutt_perror (_("Can't create temporary file"));
499 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
510 return m_strdup(tempfile);
514 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
516 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
522 if (!ctx || !r_key || !fpr)
523 return gpg_error (GPG_ERR_INV_VALUE);
525 if (strlen (fpr) < 8) /* We have at least a key ID. */
526 return gpg_error (GPG_ERR_INV_VALUE);
528 /* FIXME: We use our own context because we have to avoid the user's
529 I/O callback handlers. */
530 err = gpgme_new (&listctx);
533 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
534 err = gpgme_op_keylist_start (listctx, fpr, secret);
536 err = gpgme_op_keylist_next (listctx, r_key);
537 gpgme_release (listctx);
541 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
542 The keys must be space delimited. */
543 static gpgme_key_t *create_recipient_set(const char *s, int smime)
545 gpgme_ctx_t ctx = create_gpgme_context(smime);
546 gpgme_key_t *rset = NULL;
553 const char *p = m_strnextsp(s);
556 m_strncpy(buf, sizeof(buf), s, p - s);
557 if (p - s > 1 && p[-1] == '!') {
558 /* user wants to override the valididy of that key. */
560 buf[p - s - 1] = '\0';
561 err = gpgme_get_key2(ctx, buf, &key, 0);
563 key->uids->validity = GPGME_VALIDITY_FULL;
565 err = gpgme_get_key2(ctx, buf, &key, 0);
569 mutt_error(_("error adding recipient `%.*s': %s\n"),
570 (int)(p - s), s, gpgme_strerror(err));
575 p_realloc(&rset, rset_n + 1);
576 rset[rset_n++] = key;
581 /* NULL terminate. */
582 p_realloc(&rset, rset_n + 1);
583 rset[rset_n++] = NULL;
591 /* Make sure that the correct signer is set. Returns 0 on success. */
592 static int set_signer(gpgme_ctx_t ctx, int for_smime)
594 const char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
598 if (m_strisempty(signid))
601 err = gpgme_get_key(ctx, signid, &key, 1);
603 mutt_error(_("error getting secret key `%s': %s\n"), signid,
604 gpgme_strerror(err));
608 gpgme_signers_clear(ctx);
609 err = gpgme_signers_add(ctx, key);
610 gpgme_key_unref(key);
612 mutt_error(_("error setting secret key `%s': %s\n"), signid,
613 gpgme_strerror(err));
620 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
621 and return an allocated filename to a temporary file containing the
622 enciphered text. With USE_SMIME set to true, the smime backend is
623 used. With COMBINED_SIGNED a PGP message is signed and
624 encrypted. Returns NULL in case of error */
625 static char *encrypt_gpgme_object(gpgme_data_t plaintext, gpgme_key_t *rset,
626 int use_smime, int combined_signed)
630 gpgme_data_t ciphertext;
633 ctx = create_gpgme_context (use_smime);
635 gpgme_set_armor (ctx, 1);
637 ciphertext = create_gpgme_data ();
639 if (combined_signed) {
640 if (set_signer(ctx, use_smime)) {
641 gpgme_data_release(ciphertext);
645 err = gpgme_op_encrypt_sign(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
646 plaintext, ciphertext);
648 err = gpgme_op_encrypt(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
649 plaintext, ciphertext);
652 mutt_need_hard_redraw();
654 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
655 gpgme_data_release (ciphertext);
662 outfile = data_object_to_tempfile(ciphertext, NULL);
663 gpgme_data_release(ciphertext);
667 /* Find the "micalg" parameter from the last Gpgme operation on
668 context CTX. It is expected that this operation was a sign
669 operation. Return the algorithm name as a C string in buffer BUF
670 which must have been allocated by the caller with size BUFLEN.
671 Returns 0 on success or -1 in case of an error. The return string
672 is truncted to BUFLEN - 1. */
673 static int get_micalg(gpgme_ctx_t ctx, char *buf, ssize_t buflen)
675 gpgme_sign_result_t result = NULL;
676 const char *alg = NULL;
678 result = gpgme_op_sign_result(ctx);
679 if (result && result->signatures) {
680 alg = gpgme_hash_algo_name(result->signatures->hash_algo);
682 m_strcpy(buf, buflen, NONULL(alg));
687 static void print_time(time_t t, STATE *s)
691 setlocale(LC_TIME, "");
693 strftime(p, sizeof(p), nl_langinfo(D_T_FMT), localtime(&t));
695 strftime(p, sizeof(p), "%c", localtime(&t));
697 setlocale(LC_TIME, "C");
698 state_attach_puts(p, s);
701 /* Implementation of `sign_message'. */
703 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
704 USE_SMIME is passed as true. Returns the new body or NULL on
706 static BODY *sign_message(BODY * a, int use_smime)
713 gpgme_data_t message, signature;
715 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
717 message = body_to_data_object(a, 1);
720 signature = create_gpgme_data ();
722 ctx = create_gpgme_context (use_smime);
724 gpgme_set_armor (ctx, 1);
726 if (set_signer (ctx, use_smime)) {
727 gpgme_data_release (signature);
732 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
733 mutt_need_hard_redraw ();
734 gpgme_data_release (message);
736 gpgme_data_release (signature);
738 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
742 sigfile = data_object_to_tempfile(signature, NULL);
743 gpgme_data_release (signature);
750 t->type = TYPEMULTIPART;
751 t->subtype = m_strdup("signed");
752 t->encoding = ENC7BIT;
754 t->disposition = DISPINLINE;
756 parameter_set_boundary(&t->parameter);
757 parameter_setval(&t->parameter, "protocol",
758 use_smime ? "application/pkcs7-signature"
759 : "application/pgp-signature");
760 /* Get the micalg from gpgme. Old gpgme versions don't support this
761 for S/MIME so we assume sha-1 in this case. */
762 if (!get_micalg (ctx, buf, sizeof buf))
763 parameter_setval(&t->parameter, "micalg", buf);
765 parameter_setval(&t->parameter, "micalg", "sha1");
771 t->parts->next = body_new();
773 t->type = TYPEAPPLICATION;
775 t->subtype = m_strdup("pkcs7-signature");
776 parameter_setval(&t->parameter, "name", "smime.p7s");
777 t->encoding = ENCBASE64;
779 t->disposition = DISPATTACH;
780 t->d_filename = m_strdup("smime.p7s");
783 t->subtype = m_strdup("pgp-signature");
785 t->disposition = DISPINLINE;
786 t->encoding = ENC7BIT;
788 t->filename = sigfile;
789 t->unlink = 1; /* ok to remove this file after sending. */
794 /* Encrypt the mail body A to all keys given as space separated keyids
795 or fingerprints in KEYLIST and return the encrypted body. */
796 static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
798 char *outfile = NULL;
800 gpgme_key_t *rset = NULL;
801 gpgme_data_t plaintext;
803 rset = create_recipient_set(keylist, 0);
809 plaintext = body_to_data_object(a, 0);
815 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
816 gpgme_data_release (plaintext);
822 t->type = TYPEMULTIPART;
823 t->subtype = m_strdup("encrypted");
824 t->encoding = ENC7BIT;
826 t->disposition = DISPINLINE;
828 parameter_set_boundary(&t->parameter);
829 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
831 t->parts = body_new();
832 t->parts->type = TYPEAPPLICATION;
833 t->parts->subtype = m_strdup("pgp-encrypted");
834 t->parts->encoding = ENC7BIT;
836 t->parts->next = body_new();
837 t->parts->next->type = TYPEAPPLICATION;
838 t->parts->next->subtype = m_strdup("octet-stream");
839 t->parts->next->encoding = ENC7BIT;
840 t->parts->next->filename = outfile;
841 t->parts->next->use_disp = 1;
842 t->parts->next->disposition = DISPINLINE;
843 t->parts->next->unlink = 1; /* delete after sending the message */
844 t->parts->next->d_filename = m_strdup("msg.asc");
849 /* Encrypt the mail body A to all keys given as space separated
850 fingerprints in KEYLIST and return the S/MIME encrypted body. */
851 static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
853 char *outfile = NULL;
855 gpgme_key_t *rset = NULL;
856 gpgme_data_t plaintext;
858 rset = create_recipient_set(keylist, 1);
862 plaintext = body_to_data_object(a, 0);
868 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
869 gpgme_data_release (plaintext);
875 t->type = TYPEAPPLICATION;
876 t->subtype = m_strdup("pkcs7-mime");
877 parameter_setval(&t->parameter, "name", "smime.p7m");
878 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
879 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
881 t->disposition = DISPATTACH;
882 t->d_filename = m_strdup("smime.p7m");
883 t->filename = outfile;
884 t->unlink = 1; /*delete after sending the message */
891 /* Display the common attributes of the signature summary SUM.
892 Return 1 if there is is a severe warning.
894 static int show_sig_summary (unsigned long sum,
895 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
900 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
901 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
905 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
906 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
909 state_attach_puts (_("Warning: The key used to create the "
910 "signature expired at: "), s);
912 state_attach_puts ("\n", s);
915 state_attach_puts (_("Warning: At least one certification key "
916 "has expired\n"), s);
919 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
920 gpgme_verify_result_t result;
921 gpgme_signature_t sig;
924 result = gpgme_op_verify_result (ctx);
926 for (sig = result->signatures, i = 0; sig && (i < idx);
927 sig = sig->next, i++);
929 state_attach_puts (_("Warning: The signature expired at: "), s);
930 print_time (sig ? sig->exp_timestamp : 0, s);
931 state_attach_puts ("\n", s);
934 if ((sum & GPGME_SIGSUM_KEY_MISSING))
935 state_attach_puts (_("Can't verify due to a missing "
936 "key or certificate\n"), s);
938 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
939 state_attach_puts (_("The CRL is not available\n"), s);
943 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
944 state_attach_puts (_("Available CRL is too old\n"), s);
948 if ((sum & GPGME_SIGSUM_BAD_POLICY))
949 state_attach_puts (_("A policy requirement was not met\n"), s);
951 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
952 const char *t0 = NULL, *t1 = NULL;
953 gpgme_verify_result_t result;
954 gpgme_signature_t sig;
957 state_attach_puts (_("A system error occurred"), s);
959 /* Try to figure out some more detailed system error information. */
960 result = gpgme_op_verify_result (ctx);
961 for (sig = result->signatures, i = 0; sig && (i < idx);
962 sig = sig->next, i++);
965 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
969 state_attach_puts (": ", s);
971 state_attach_puts (t0, s);
972 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
974 state_attach_puts (",", s);
975 state_attach_puts (t1, s);
978 state_attach_puts ("\n", s);
985 static void show_fingerprint (gpgme_key_t key, STATE * state)
990 const char *prefix = _("Fingerprint: ");
995 s = key->subkeys ? key->subkeys->fpr : NULL;
998 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
1000 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
1001 buf = p_new(char, bufsize);
1002 m_strcpy(buf, bufsize, prefix);
1003 p = buf + m_strlen(buf);
1004 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
1005 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
1016 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
1019 *p++ = is_pgp ? ' ' : ':';
1020 if (is_pgp && i == 7)
1025 /* just in case print remaining odd digits */
1030 state_attach_puts (buf, state);
1034 /* Show the valididy of a key used for one signature. */
1035 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
1037 gpgme_verify_result_t result = NULL;
1038 gpgme_signature_t sig = NULL;
1039 const char *txt = NULL;
1041 result = gpgme_op_verify_result (ctx);
1043 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
1045 switch (sig ? sig->validity : 0) {
1046 case GPGME_VALIDITY_UNKNOWN:
1047 txt = _("WARNING: We have NO indication whether "
1048 "the key belongs to the person named " "as shown above\n");
1050 case GPGME_VALIDITY_UNDEFINED:
1052 case GPGME_VALIDITY_NEVER:
1053 txt = _("WARNING: The key does NOT BELONG to "
1054 "the person named as shown above\n");
1056 case GPGME_VALIDITY_MARGINAL:
1057 txt = _("WARNING: It is NOT certain that the key "
1058 "belongs to the person named as shown above\n");
1060 case GPGME_VALIDITY_FULL:
1061 case GPGME_VALIDITY_ULTIMATE:
1066 state_attach_puts (txt, s);
1069 /* Show information about one signature. This fucntion is called with
1070 the context CTX of a sucessful verification operation and the
1071 enumerator IDX which should start at 0 and incremete for each
1074 Return values are: 0 for normal procession, 1 for a bad signature,
1075 2 for a signature with a warning or -1 for no more signature. */
1076 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
1079 const char *fpr, *uid;
1080 gpgme_key_t key = NULL;
1081 int i, anybad = 0, anywarn = 0;
1083 gpgme_user_id_t uids = NULL;
1084 gpgme_verify_result_t result;
1085 gpgme_signature_t sig;
1086 gpgme_error_t err = GPG_ERR_NO_ERROR;
1088 result = gpgme_op_verify_result (ctx);
1090 /* FIXME: this code should use a static variable and remember
1091 the current position in the list of signatures, IMHO.
1094 for (i = 0, sig = result->signatures; sig && (i < idx);
1095 i++, sig = sig->next);
1097 return -1; /* Signature not found. */
1099 if (signature_key) {
1100 gpgme_key_unref(signature_key);
1101 signature_key = NULL;
1104 created = sig->timestamp;
1108 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1111 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1113 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1115 signature_key = key;
1118 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1119 error. Do it here to avoid a double free. */
1123 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1125 state_attach_puts (_("Error getting key information: "), s);
1126 state_attach_puts (gpg_strerror (err), s);
1127 state_attach_puts ("\n", s);
1130 else if ((sum & GPGME_SIGSUM_GREEN)) {
1131 state_attach_puts (_("Good signature from: "), s);
1132 state_attach_puts (uid, s);
1133 state_attach_puts ("\n", s);
1134 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1136 /* Skip primary UID. */
1140 state_attach_puts (_(" aka: "), s);
1141 state_attach_puts (uids->uid, s);
1142 state_attach_puts ("\n", s);
1144 state_attach_puts (_(" created: "), s);
1145 print_time (created, s);
1146 state_attach_puts ("\n", s);
1147 if (show_sig_summary (sum, ctx, key, idx, s))
1149 show_one_sig_validity (ctx, idx, s);
1151 else if ((sum & GPGME_SIGSUM_RED)) {
1152 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1153 state_attach_puts (uid, s);
1154 state_attach_puts ("\n", s);
1155 show_sig_summary (sum, ctx, key, idx, s);
1157 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1158 signature, so we display what a PGP user expects: The name,
1159 fingerprint and the key validity (which is neither fully or
1161 state_attach_puts (_("Good signature from: "), s);
1162 state_attach_puts (uid, s);
1163 state_attach_puts ("\n", s);
1164 state_attach_puts (_(" created: "), s);
1165 print_time (created, s);
1166 state_attach_puts ("\n", s);
1167 show_one_sig_validity (ctx, idx, s);
1168 show_fingerprint (key, s);
1169 if (show_sig_summary (sum, ctx, key, idx, s))
1172 else { /* can't decide (yellow) */
1174 state_attach_puts (_("Error checking signature"), s);
1175 state_attach_puts ("\n", s);
1176 show_sig_summary (sum, ctx, key, idx, s);
1179 if (key != signature_key)
1180 gpgme_key_unref(key);
1183 return anybad ? 1 : anywarn ? 2 : 0;
1186 /* Do the actual verification step. With IS_SMIME set to true we
1187 assume S/MIME (surprise!) */
1188 static int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
1194 gpgme_data_t signature, message;
1196 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1200 /* We need to tell gpgme about the encoding because the backend can't
1201 auto-detect plain base-64 encoding which is used by S/MIME. */
1203 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1205 err = gpgme_data_new_from_stream(&message, fp);
1207 gpgme_data_release (signature);
1208 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1211 ctx = create_gpgme_context (is_smime);
1213 /* Note: We don't need a current time output because GPGME avoids
1214 such an attack by separating the meta information from the
1216 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1218 err = gpgme_op_verify (ctx, signature, message, NULL);
1222 snprintf (buf, sizeof (buf) - 1,
1223 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1224 state_attach_puts (buf, s);
1226 else { /* Verification succeeded, see what the result is. */
1230 if (signature_key) {
1231 gpgme_key_unref(signature_key);
1232 signature_key = NULL;
1235 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1246 gpgme_verify_result_t result;
1247 gpgme_sig_notation_t notation;
1248 gpgme_signature_t sig;
1250 result = gpgme_op_verify_result (ctx);
1252 for (sig = result->signatures; sig; sig = sig->next) {
1253 if (sig->notations) {
1254 state_attach_puts ("*** Begin Notation (signature by: ", s);
1255 state_attach_puts (sig->fpr, s);
1256 state_attach_puts (") ***\n", s);
1257 for (notation = sig->notations; notation; notation = notation->next)
1259 if (notation->name) {
1260 state_attach_puts (notation->name, s);
1261 state_attach_puts ("=", s);
1263 if (notation->value) {
1264 state_attach_puts (notation->value, s);
1265 if (!(*notation->value
1266 && (notation->value[m_strlen(notation->value) - 1] ==
1268 state_attach_puts ("\n", s);
1271 state_attach_puts ("*** End Notation ***\n", s);
1277 gpgme_release (ctx);
1279 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1281 return badsig ? 1 : anywarn ? 2 : 0;
1284 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1285 IS_SMIME) with body A described further by state S. Write
1286 plaintext out to file FPOUT and return a new body. For PGP returns
1287 a flag in R_IS_SIGNED to indicate whether this is a combined
1288 encrypted and signed message, for S/MIME it returns true when it is
1289 not a encrypted but a signed message. */
1291 decrypt_part(BODY *a, STATE *s, FILE *fpout, int is_smime, int *r_is_signed)
1297 gpgme_data_t ciphertext, plaintext;
1298 int maybe_signed = 0;
1305 ctx = create_gpgme_context (is_smime);
1308 /* Make a data object from the body, create context etc. */
1309 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1312 plaintext = create_gpgme_data ();
1314 /* Do the decryption or the verification in case of the S/MIME hack. */
1315 if ((!is_smime) || maybe_signed) {
1317 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1318 else if (maybe_signed)
1319 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1322 /* Check wether signatures have been verified. */
1323 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1325 if (verify_result->signatures)
1330 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1331 gpgme_data_release (ciphertext);
1333 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1334 /* Check whether this might be a signed message despite what
1335 the mime header told us. Retry then. gpgsm returns the
1336 error information "unsupported Algorithm '?'" but gpgme
1337 will not store this unknown algorithm, thus we test that
1338 it has not been set. */
1339 gpgme_decrypt_result_t result;
1341 result = gpgme_op_decrypt_result (ctx);
1342 if (!result->unsupported_algorithm) {
1344 gpgme_data_release (plaintext);
1348 mutt_need_hard_redraw ();
1349 if ((s->flags & M_DISPLAY)) {
1352 snprintf (buf, sizeof (buf) - 1,
1353 _("[-- Error: decryption failed: %s --]\n\n"),
1354 gpgme_strerror (err));
1355 state_attach_puts (buf, s);
1357 gpgme_data_release (plaintext);
1358 gpgme_release (ctx);
1361 mutt_need_hard_redraw ();
1363 /* Read the output from GPGME, and make sure to change CRLF to LF,
1364 otherwise read_mime_header has a hard time parsing the message. */
1365 if (data_object_to_stream (plaintext, fpout)) {
1366 gpgme_data_release (plaintext);
1367 gpgme_release (ctx);
1370 gpgme_data_release (plaintext);
1372 a->is_signed_data = 0;
1378 a->is_signed_data = 1;
1380 *r_is_signed = -1; /* A signature exists. */
1382 if ((s->flags & M_DISPLAY))
1383 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1384 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1390 if (!anybad && idx && r_is_signed && *r_is_signed)
1391 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1393 if ((s->flags & M_DISPLAY))
1394 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1396 gpgme_release (ctx);
1401 tattach = mutt_read_mime_header (fpout, 0);
1404 * Need to set the length of this body part.
1406 fstat (fileno (fpout), &info);
1407 tattach->length = info.st_size - tattach->offset;
1409 tattach->warnsig = anywarn;
1411 /* See if we need to recurse on this MIME part. */
1412 mutt_parse_part (fpout, tattach);
1418 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1419 the stream in CUR and FPOUT. Returns 0 on success. */
1420 int crypt_pgp_decrypt_mime (FILE * fpin, FILE **fpout, BODY *b, BODY **cur)
1423 BODY *first_part = b;
1426 first_part->goodsig = 0;
1427 first_part->warnsig = 0;
1429 if (!mutt_is_multipart_encrypted(b) || !b->parts || !b->parts->next)
1438 mutt_perror (_("Can't create temporary file"));
1442 *cur = decrypt_part(b, &s, *fpout, 0, &is_signed);
1444 first_part->goodsig = is_signed > 0;
1445 return *cur ? 0 : -1;
1449 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1450 the stream in CUR and FPOUT. Returns 0 on success. */
1451 int crypt_smime_decrypt_mime(FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
1456 long saved_b_offset;
1457 ssize_t saved_b_length;
1460 if (!mutt_is_application_smime (b))
1466 /* Decode the body - we need to pass binary CMS to the
1467 backend. The backend allows for Base64 encoded data but it does
1468 not allow for QP which I have seen in some messages. So better
1470 saved_b_type = b->type;
1471 saved_b_offset = b->offset;
1472 saved_b_length = b->length;
1475 fseeko (s.fpin, b->offset, 0);
1478 mutt_perror (_("Can't create temporary file"));
1483 mutt_decode_attachment (b, &s);
1485 b->length = ftello (s.fpout);
1494 mutt_perror (_("Can't create temporary file"));
1498 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1500 (*cur)->goodsig = is_signed > 0;
1501 b->type = saved_b_type;
1502 b->length = saved_b_length;
1503 b->offset = saved_b_offset;
1506 if (*cur && !is_signed && !(*cur)->parts
1507 && mutt_is_application_smime (*cur)) {
1508 /* Assume that this is a opaque signed s/mime message. This is
1509 an ugly way of doing it but we have anyway a problem with
1510 arbitrary encoded S/MIME messages: Only the outer part may be
1511 encrypted. The entire mime parsing should be revamped,
1512 probably by keeping the temportary files so that we don't
1513 need to decrypt them all the time. Inner parts of an
1514 encrypted part can then pint into this file and tehre won't
1515 never be a need to decrypt again. This needs a partial
1516 rewrite of the MIME engine. */
1520 saved_b_type = bb->type;
1521 saved_b_offset = bb->offset;
1522 saved_b_length = bb->length;
1525 fseeko (s.fpin, bb->offset, 0);
1528 mutt_perror (_("Can't create temporary file"));
1533 mutt_decode_attachment (bb, &s);
1535 bb->length = ftello (s.fpout);
1545 mutt_perror (_("Can't create temporary file"));
1549 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1551 tmp_b->goodsig = is_signed > 0;
1552 bb->type = saved_b_type;
1553 bb->length = saved_b_length;
1554 bb->offset = saved_b_offset;
1557 body_list_wipe(cur);
1560 return *cur ? 0 : -1;
1565 pgp_check_traditional_one_body(FILE *fp, BODY *b, int tagged_only)
1567 char tempfile[_POSIX_PATH_MAX];
1568 char buf[HUGE_STRING];
1575 if (b->type != TYPETEXT)
1578 if (tagged_only && !b->tagged)
1581 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
1582 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1587 if ((tfp = fopen(tempfile, "r")) == NULL) {
1592 while (fgets (buf, sizeof (buf), tfp)) {
1593 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1594 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1596 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1606 /* fix the content type */
1608 parameter_setval(&b->parameter, "format", "fixed");
1609 parameter_setval(&b->parameter, "x-action",
1610 enc ? "pgp-encrypted" : "pgp-signed");
1614 int crypt_pgp_check_traditional(FILE *fp, BODY *b, int tagged_only)
1618 for (; b; b = b->next) {
1619 if (is_multipart(b))
1620 rv |= crypt_pgp_check_traditional(fp, b->parts, tagged_only);
1621 if (b->type == TYPETEXT) {
1623 if ((r = mutt_is_application_pgp(b))) {
1626 rv |= pgp_check_traditional_one_body(fp, b, tagged_only);
1635 Copy a clearsigned message, and strip the signature and PGP's
1638 XXX - charset handling: We assume that it is safe to do
1639 character set decoding first, dash decoding second here, while
1640 we do it the other way around in the main handler.
1642 (Note that we aren't worse than Outlook & Cie in this, and also
1643 note that we can successfully handle anything produced by any
1644 existing versions of mutt.) */
1645 static void copy_clearsigned(gpgme_data_t data, STATE * s, char *charset)
1647 char buf[HUGE_STRING];
1648 short complete, armor_header;
1653 fname = data_object_to_tempfile(data, &fp);
1659 fc = fgetconv_open (fp, charset, mod_cset.charset, M_ICONV_HOOK_FROM);
1661 for (complete = 1, armor_header = 1;
1662 fgetconvs (buf, sizeof (buf), fc) != NULL;
1663 complete = strchr (buf, '\n') != NULL) {
1666 state_puts (buf, s);
1670 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1680 state_puts (s->prefix, s);
1682 if (buf[0] == '-' && buf[1] == ' ')
1683 state_puts (buf + 2, s);
1685 state_puts (buf, s);
1688 fgetconv_close (&fc);
1692 /* Support for classic_application/pgp */
1693 int crypt_pgp_application_pgp_handler(BODY *m, STATE *s)
1695 int needpass = -1, pgp_keyblock = 0;
1699 off_t last_pos, offset;
1700 char buf[HUGE_STRING];
1701 FILE *pgpout = NULL;
1703 gpgme_error_t err = 0;
1704 gpgme_data_t armored_data = NULL;
1706 short maybe_goodsig = 1;
1707 short have_any_sigs = 0;
1709 char body_charset[STRING]; /* Only used for clearsigned messages. */
1711 /* For clearsigned messages we won't be able to get a character set
1712 but we know that this may only be text thus we assume Latin-1
1714 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1715 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1717 fseeko (s->fpin, m->offset, 0);
1718 last_pos = m->offset;
1720 for (bytes = m->length; bytes > 0;) {
1721 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1724 offset = ftello (s->fpin);
1725 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1728 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1730 start_pos = last_pos;
1732 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1734 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1738 else if (!m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1743 /* XXX - we may wish to recode here */
1745 state_puts (s->prefix, s);
1746 state_puts (buf, s);
1750 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1752 /* Copy PGP material to an data container */
1753 armored_data = create_gpgme_data ();
1754 gpgme_data_write (armored_data, buf, m_strlen(buf));
1755 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1756 offset = ftello (s->fpin);
1757 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1760 gpgme_data_write (armored_data, buf, m_strlen(buf));
1762 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1764 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1765 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1770 /* Invoke PGP if needed */
1771 if (!clearsign || (s->flags & M_VERIFY)) {
1772 unsigned int sig_stat = 0;
1773 gpgme_data_t plaintext;
1776 plaintext = create_gpgme_data ();
1777 ctx = create_gpgme_context (0);
1780 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1782 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1783 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1784 /* Decrypt verify can't handle signed only messages. */
1785 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1786 ? gpgme_error_from_errno (errno) : 0;
1787 /* Must release plaintext so that we supply an
1788 uninitialized object. */
1789 gpgme_data_release (plaintext);
1790 plaintext = create_gpgme_data ();
1791 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1798 snprintf (errbuf, sizeof (errbuf) - 1,
1799 _("Error: decryption/verification failed: %s\n"),
1800 gpgme_strerror (err));
1801 state_attach_puts (errbuf, s);
1803 else { /* Decryption/Verification succeeded */
1807 /* Check wether signatures have been verified. */
1808 gpgme_verify_result_t verify_result;
1810 verify_result = gpgme_op_verify_result (ctx);
1811 if (verify_result->signatures)
1817 if ((s->flags & M_DISPLAY) && sig_stat) {
1822 state_attach_puts (_("[-- Begin signature "
1823 "information --]\n"), s);
1826 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1835 state_attach_puts (_("[-- End signature "
1836 "information --]\n\n"), s);
1839 tmpfname = data_object_to_tempfile(plaintext, &pgpout);
1842 state_attach_puts (_("Error: copy data failed\n"), s);
1846 p_delete(&tmpfname);
1849 gpgme_release (ctx);
1853 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1854 * outputs utf-8 cleartext. This may not always be true, but it
1855 * seems to be a reasonable guess.
1858 if (s->flags & M_DISPLAY) {
1860 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1861 else if (pgp_keyblock)
1862 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1864 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1868 copy_clearsigned (armored_data, s, body_charset);
1875 fc = fgetconv_open (pgpout, "utf-8", mod_cset.charset, 0);
1876 while ((c = fgetconv (fc)) != EOF) {
1878 if (c == '\n' && s->prefix)
1879 state_puts (s->prefix, s);
1881 fgetconv_close (&fc);
1884 if (s->flags & M_DISPLAY) {
1885 state_putc ('\n', s);
1887 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1888 else if (pgp_keyblock)
1889 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1891 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1899 /* XXX - we may wish to recode here */
1901 state_puts (s->prefix, s);
1902 state_puts (buf, s);
1906 m->goodsig = (maybe_goodsig && have_any_sigs);
1908 if (needpass == -1) {
1909 state_attach_puts (_("[-- Error: could not find beginning"
1910 " of PGP message! --]\n\n"), s);
1916 /* MIME handler for pgp/mime encrypted messages. */
1917 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1919 char tempfile[_POSIX_PATH_MAX];
1922 BODY *orig_body = a;
1927 if (!a || a->type != TYPEAPPLICATION || !a->subtype
1928 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
1929 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
1930 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
1931 if (s->flags & M_DISPLAY)
1932 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
1937 /* Move forward to the application/pgp-encrypted body. */
1940 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
1942 if (s->flags & M_DISPLAY)
1943 state_attach_puts (_("[-- Error: could not create temporary file! "
1948 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
1950 tattach->goodsig = is_signed > 0;
1952 if (s->flags & M_DISPLAY)
1953 state_attach_puts (is_signed ?
1955 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
1956 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
1959 FILE *savefp = s->fpin;
1962 rc = mutt_body_handler (tattach, s);
1967 * if a multipart/signed is the _only_ sub-part of a
1968 * multipart/encrypted, cache signature verification
1971 if (mutt_is_multipart_signed (tattach) && !tattach->next)
1972 orig_body->goodsig |= tattach->goodsig;
1974 if (s->flags & M_DISPLAY) {
1975 state_puts ("\n", s);
1976 state_attach_puts (is_signed ?
1978 ("[-- End of PGP/MIME signed and encrypted data --]\n")
1979 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
1982 body_list_wipe(&tattach);
1986 mutt_unlink (tempfile);
1990 /* Support for application/smime */
1991 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
1993 char tempfile[_POSIX_PATH_MAX];
2000 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2002 if (s->flags & M_DISPLAY)
2003 state_attach_puts (_("[-- Error: could not create temporary file! "
2008 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
2010 tattach->goodsig = is_signed > 0;
2012 if (s->flags & M_DISPLAY)
2013 state_attach_puts (is_signed ?
2014 _("[-- The following data is S/MIME signed --]\n\n") :
2015 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
2018 FILE *savefp = s->fpin;
2021 rc = mutt_body_handler (tattach, s);
2026 * if a multipart/signed is the _only_ sub-part of a
2027 * multipart/encrypted, cache signature verification
2030 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
2031 if (!(a->goodsig = tattach->goodsig))
2032 a->warnsig = tattach->warnsig;
2034 else if (tattach->goodsig) {
2036 a->warnsig = tattach->warnsig;
2039 if (s->flags & M_DISPLAY) {
2040 state_puts ("\n", s);
2041 state_attach_puts (is_signed ?
2042 _("[-- End of S/MIME signed data --]\n") :
2043 _("[-- End of S/MIME encrypted data --]\n"), s);
2046 body_list_wipe(&tattach);
2050 mutt_unlink (tempfile);
2056 * Format an entry on the CRYPT key selection menu.
2059 * %k key id %K key id of the principal key
2061 * %a algorithm %A algorithm of the princ. key
2062 * %l length %L length of the princ. key
2063 * %f flags %F flags of the princ. key
2064 * %c capabilities %C capabilities of the princ. key
2065 * %t trust/validity of the key-uid association
2067 * %[...] date of key using strftime(3)
2071 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
2072 const char *src, const char *prefix,
2073 const char *ifstr, const char *elstr,
2074 anytype data, format_flag flags)
2077 crypt_entry_t *entry;
2080 int optional = (flags & M_FORMAT_OPTIONAL);
2081 const char *s = NULL;
2087 /* if (isupper ((unsigned char) op)) */
2090 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
2093 switch (ascii_tolower (op)) {
2097 char buf2[STRING], *p;
2113 while (len > 0 && *cp != ']') {
2122 break; /* not enough space */
2132 if (do_locales && Locale)
2133 setlocale (LC_TIME, Locale);
2138 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2139 tt = key->kobj->subkeys->timestamp;
2141 tm = localtime (&tt);
2143 strftime (buf2, sizeof (buf2), dest, tm);
2146 setlocale (LC_TIME, "C");
2148 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2149 snprintf (dest, destlen, fmt, buf2);
2156 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2157 snprintf (dest, destlen, fmt, entry->num);
2162 /* fixme: we need a way to distinguish between main and subkeys.
2163 Store the idx in entry? */
2164 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2165 snprintf (dest, destlen, fmt, crypt_keyid (key));
2170 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2171 snprintf (dest, destlen, fmt, key->uid);
2176 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2177 if (key->kobj->subkeys)
2178 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2181 snprintf (dest, destlen, fmt, s);
2186 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2187 if (key->kobj->subkeys)
2188 val = key->kobj->subkeys->length;
2191 snprintf (dest, destlen, fmt, val);
2196 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2197 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2199 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2204 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2205 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2207 else if (!(kflags & (KEYFLAG_ABILITIES)))
2211 if ((kflags & KEYFLAG_ISX509))
2214 gpgme_user_id_t uid = NULL;
2217 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2218 i++, uid = uid->next);
2220 switch (uid->validity) {
2221 case GPGME_VALIDITY_UNDEFINED:
2224 case GPGME_VALIDITY_NEVER:
2227 case GPGME_VALIDITY_MARGINAL:
2230 case GPGME_VALIDITY_FULL:
2233 case GPGME_VALIDITY_ULTIMATE:
2236 case GPGME_VALIDITY_UNKNOWN:
2242 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2243 snprintf (dest, destlen, fmt, s ? *s : 'B');
2246 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2247 snprintf (dest, destlen, fmt,
2248 gpgme_get_protocol_name (key->kobj->protocol));
2255 if (flags & M_FORMAT_OPTIONAL)
2256 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2257 mutt_attach_fmt, data, 0);
2261 /* Used by the display fucntion to format a line. */
2262 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2264 cryptkey_t **cryptkey_table = (cryptkey_t **) menu->data;
2265 crypt_entry_t entry;
2267 entry.key = cryptkey_table[num];
2268 entry.num = num + 1;
2270 m_strformat(s, l, getmaxx(main_w), mod_crypt.pgp_entry_format,
2271 crypt_entry_fmt, &entry, 0);
2274 /* Compare two addresses and the keyid to be used for sorting. */
2275 static int _crypt_compare_address (const void *a, const void *b)
2277 cryptkey_t **s = (cryptkey_t **) a;
2278 cryptkey_t **t = (cryptkey_t **) b;
2281 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2284 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2287 static int crypt_compare_address (const void *a, const void *b)
2289 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2290 : _crypt_compare_address (a, b));
2294 /* Compare two key IDs and the addresses to be used for sorting. */
2295 static int _crypt_compare_keyid (const void *a, const void *b)
2297 cryptkey_t **s = (cryptkey_t **) a;
2298 cryptkey_t **t = (cryptkey_t **) b;
2301 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2304 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2307 static int crypt_compare_keyid (const void *a, const void *b)
2309 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2310 : _crypt_compare_keyid (a, b));
2313 /* Compare 2 creation dates and the addresses. For sorting. */
2314 static int _crypt_compare_date (const void *a, const void *b)
2316 cryptkey_t **s = (cryptkey_t **) a;
2317 cryptkey_t **t = (cryptkey_t **) b;
2318 unsigned long ts = 0, tt = 0;
2320 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2321 ts = (*s)->kobj->subkeys->timestamp;
2322 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2323 tt = (*t)->kobj->subkeys->timestamp;
2330 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2333 static int crypt_compare_date (const void *a, const void *b)
2335 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2336 : _crypt_compare_date (a, b));
2339 /* Compare two trust values, the key length, the creation dates. the
2340 addresses and the key IDs. For sorting. */
2341 static int _crypt_compare_trust (const void *a, const void *b)
2343 cryptkey_t **s = (cryptkey_t **) a;
2344 cryptkey_t **t = (cryptkey_t **) b;
2345 unsigned long ts = 0, tt = 0;
2348 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2349 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2352 if ((*s)->kobj->uids)
2353 ts = (*s)->kobj->uids->validity;
2354 if ((*t)->kobj->uids)
2355 tt = (*t)->kobj->uids->validity;
2356 if ((r = (tt - ts)))
2359 if ((*s)->kobj->subkeys)
2360 ts = (*s)->kobj->subkeys->length;
2361 if ((*t)->kobj->subkeys)
2362 tt = (*t)->kobj->subkeys->length;
2366 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2367 ts = (*s)->kobj->subkeys->timestamp;
2368 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2369 tt = (*t)->kobj->subkeys->timestamp;
2375 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2377 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2380 static int crypt_compare_trust (const void *a, const void *b)
2382 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2383 : _crypt_compare_trust (a, b));
2386 /* Print the X.500 Distinguished Name part KEY from the array of parts
2388 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2392 for (; dn->key; dn++) {
2393 if (!m_strcmp(dn->key, key)) {
2396 print_utf8 (fp, dn->value, m_strlen(dn->value));
2403 /* Print all parts of a DN in a standard sequence. */
2404 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2406 const char *stdpart[] = {
2407 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2409 int any = 0, any2 = 0, i;
2411 for (i = 0; stdpart[i]; i++) {
2414 any = print_dn_part (fp, dn, stdpart[i]);
2416 /* now print the rest without any specific ordering */
2417 for (; dn->key; dn++) {
2418 for (i = 0; stdpart[i]; i++) {
2419 if (!m_strcmp(dn->key, stdpart[i]))
2427 any = print_dn_part (fp, dn, dn->key);
2436 /* Parse an RDN; this is a helper to parse_dn(). */
2437 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2438 const unsigned char *string)
2440 const unsigned char *s, *s1;
2444 /* parse attributeType */
2445 for (s = string + 1; *s && *s != '='; s++);
2447 return NULL; /* error */
2450 return NULL; /* empty key */
2451 array->key = p_dupstr(string, n );
2452 p = (unsigned char *) array->key;
2455 if (*string == '#') { /* hexstring */
2457 for (s = string; hexval(*s) >= 0; s++)
2461 return NULL; /* empty or odd number of digits */
2463 p = p_new(unsigned char, n + 1);
2464 array->value = (char *) p;
2465 for (s1 = string; n; s1 += 2, n--)
2466 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2469 else { /* regular v3 quoted string */
2470 for (n = 0, s = string; *s; s++) {
2471 if (*s == '\\') { /* pair */
2473 if (*s == ',' || *s == '=' || *s == '+'
2474 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2475 || *s == '\\' || *s == '"' || *s == ' ')
2477 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2482 return NULL; /* invalid escape sequence */
2485 return NULL; /* invalid encoding */
2486 else if (*s == ',' || *s == '=' || *s == '+'
2487 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2493 p = p_new(unsigned char, n + 1);
2494 array->value = (char *) p;
2495 for (s = string; n; s++, n--) {
2498 if (hexval(*s) >= 0) {
2499 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2514 /* Parse a DN and return an array-ized one. This is not a validating
2515 parser and it does not support any old-stylish syntax; gpgme is
2516 expected to return only rfc2253 compatible strings. */
2517 static struct dn_array_s *parse_dn (const unsigned char *string)
2519 struct dn_array_s *array;
2520 ssize_t arrayidx, arraysize;
2523 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2524 array = p_new(struct dn_array_s, arraysize + 1);
2527 while (*string == ' ')
2531 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2532 struct dn_array_s *a2;
2535 a2 = p_new(struct dn_array_s, arraysize + 1);
2536 for (i = 0; i < arrayidx; i++) {
2537 a2[i].key = array[i].key;
2538 a2[i].value = array[i].value;
2543 array[arrayidx].key = NULL;
2544 array[arrayidx].value = NULL;
2545 string = parse_dn_part (array + arrayidx, string);
2549 while (*string == ' ')
2551 if (*string && *string != ',' && *string != ';' && *string != '+')
2552 goto failure; /* invalid delimiter */
2556 array[arrayidx].key = NULL;
2557 array[arrayidx].value = NULL;
2561 for (i = 0; i < arrayidx; i++) {
2562 p_delete(&array[i].key);
2563 p_delete(&array[i].value);
2570 /* Print a nice representation of the USERID and make sure it is
2571 displayed in a proper way, which does mean to reorder some parts
2572 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2573 functions. It is utf-8 encoded. */
2574 static void parse_and_print_user_id(FILE * fp, const char *userid)
2579 if (*userid == '<') {
2580 s = strchr (userid + 1, '>');
2582 print_utf8 (fp, userid + 1, s - userid - 1);
2584 else if (*userid == '(')
2585 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2586 else if (*userid & ~127 || __m_strdigits[(int)*userid] == 255)
2587 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2589 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2592 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2594 print_dn_parts (fp, dn);
2595 for (i = 0; dn[i].key; i++) {
2596 p_delete(&dn[i].key);
2597 p_delete(&dn[i].value);
2605 KEY_CAP_CAN_ENCRYPT,
2610 static unsigned int key_check_cap(gpgme_key_t key, key_cap_t cap)
2612 gpgme_subkey_t subkey = NULL;
2613 unsigned int ret = 0;
2616 case KEY_CAP_CAN_ENCRYPT:
2617 if (!(ret = key->can_encrypt))
2618 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2619 if ((ret = subkey->can_encrypt))
2622 case KEY_CAP_CAN_SIGN:
2623 if (!(ret = key->can_sign))
2624 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2625 if ((ret = subkey->can_sign))
2628 case KEY_CAP_CAN_CERTIFY:
2629 if (!(ret = key->can_certify))
2630 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2631 if ((ret = subkey->can_certify))
2640 /* Print verbose information about a key or certificate to FP. */
2641 static void print_key_info (gpgme_key_t key, FILE * fp)
2644 const char *s = NULL, *s2 = NULL;
2647 char shortbuf[STRING];
2648 unsigned long aval = 0;
2652 gpgme_user_id_t uid = NULL;
2655 setlocale (LC_TIME, Locale);
2657 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2659 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2664 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2667 fputs (_("[Invalid]"), fp);
2671 print_utf8 (fp, s, m_strlen(s));
2673 parse_and_print_user_id (fp, s);
2677 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2678 tt = key->subkeys->timestamp;
2680 tm = localtime (&tt);
2682 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2684 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2686 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2689 if (key->subkeys && (key->subkeys->expires > 0)) {
2690 tt = key->subkeys->expires;
2692 tm = localtime (&tt);
2694 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2696 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2698 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2702 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2706 s2 = is_pgp ? "PGP" : "X.509";
2709 aval = key->subkeys->length;
2711 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2713 fprintf (fp, _("Key Usage .: "));
2716 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2717 fprintf (fp, "%s%s", delim, _("encryption"));
2720 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2721 fprintf (fp, "%s%s", delim, _("signing"));
2724 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2725 fprintf (fp, "%s%s", delim, _("certification"));
2731 s = key->subkeys->fpr;
2732 fputs (_("Fingerprint: "), fp);
2733 if (is_pgp && m_strlen(s) == 40) {
2734 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2739 putc (is_pgp ? ' ' : ':', fp);
2740 if (is_pgp && i == 4)
2745 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2748 putc (is_pgp ? ' ' : ':', fp);
2749 if (is_pgp && i == 7)
2753 fprintf (fp, "%s\n", s);
2756 if (key->issuer_serial) {
2757 s = key->issuer_serial;
2759 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2762 if (key->issuer_name) {
2763 s = key->issuer_name;
2765 fprintf (fp, _("Issued By .: "));
2766 parse_and_print_user_id (fp, s);
2771 /* For PGP we list all subkeys. */
2773 gpgme_subkey_t subkey = NULL;
2775 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2779 if (m_strlen(s) == 16)
2780 s += 8; /* display only the short keyID */
2781 fprintf (fp, _("Subkey ....: 0x%s"), s);
2782 if (subkey->revoked) {
2784 fputs (_("[Revoked]"), fp);
2786 if (subkey->invalid) {
2788 fputs (_("[Invalid]"), fp);
2790 if (subkey->expired) {
2792 fputs (_("[Expired]"), fp);
2794 if (subkey->disabled) {
2796 fputs (_("[Disabled]"), fp);
2800 if (subkey->timestamp > 0) {
2801 tt = subkey->timestamp;
2803 tm = localtime (&tt);
2805 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2807 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2809 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2812 if (subkey->expires > 0) {
2813 tt = subkey->expires;
2815 tm = localtime (&tt);
2817 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2819 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2821 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2825 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2830 aval = subkey->length;
2834 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2836 fprintf (fp, _("Key Usage .: "));
2839 if (subkey->can_encrypt) {
2840 fprintf (fp, "%s%s", delim, _("encryption"));
2843 if (subkey->can_sign) {
2844 fprintf (fp, "%s%s", delim, _("signing"));
2847 if (subkey->can_certify) {
2848 fprintf (fp, "%s%s", delim, _("certification"));
2856 setlocale (LC_TIME, "C");
2860 /* Show detailed information about the selected key */
2861 static void verify_key (cryptkey_t * key)
2864 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2866 gpgme_ctx_t listctx = NULL;
2868 gpgme_key_t k = NULL;
2871 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2873 mutt_perror (_("Can't create temporary file"));
2876 mutt_message _("Collecting data...");
2878 print_key_info (key->kobj, fp);
2880 err = gpgme_new (&listctx);
2882 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2883 gpgme_strerror (err));
2886 if ((key->flags & KEYFLAG_ISX509))
2887 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2891 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2893 err = gpgme_op_keylist_start (listctx, s, 0);
2897 err = gpgme_op_keylist_next (listctx, &k);
2899 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2902 gpgme_op_keylist_end (listctx);
2904 print_key_info (k, fp);
2907 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2914 gpgme_release (listctx);
2916 mutt_clear_error ();
2917 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2918 mutt_pager(cmd, tempfile, 0, NULL);
2921 /* Implementation of `findkeys'. */
2923 static void add_hints(string_array *arr, const char *s)
2929 int l = strcspn(s, " ,.:\"()<>\n");
2930 string_array_append(arr, p_dupstr(s, l));
2932 s += strspn(s, " ,.:\"()<>\n");
2936 /* Return a list of keys which are candidates for the selection. */
2938 get_candidates(string_array *hints, unsigned int app, int secret)
2940 cryptkey_t *res = NULL, **kend = &res;
2945 if (hints->len <= 0)
2947 string_array_append(hints, NULL);
2948 ctx = create_gpgme_context(0);
2950 if ((app & APPLICATION_PGP)) {
2951 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2954 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2955 gpgme_strerror(err));
2960 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2961 gpgme_user_id_t uid = NULL;
2962 unsigned int flags = 0;
2965 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2966 flags |= KEYFLAG_CANENCRYPT;
2967 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2968 flags |= KEYFLAG_CANSIGN;
2970 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2971 cryptkey_t *k = p_new(cryptkey_t, 1);
2980 if (gpg_err_code(err) != GPG_ERR_EOF)
2981 mutt_error(_("gpgme_op_keylist_next failed: %s"), gpgme_strerror(err));
2982 gpgme_op_keylist_end(ctx);
2985 if ((app & APPLICATION_SMIME)) {
2986 gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
2987 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2990 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2991 gpgme_strerror(err));
2996 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2997 gpgme_user_id_t uid = NULL;
2998 unsigned int flags = KEYFLAG_ISX509;
3001 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
3002 flags |= KEYFLAG_CANENCRYPT;
3003 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
3004 flags |= KEYFLAG_CANSIGN;
3006 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3007 cryptkey_t *k = p_new(cryptkey_t, 1);
3016 if (gpg_err_code(err) != GPG_ERR_EOF)
3017 mutt_error(_("gpgme_op_keylist_next failed: %s"),
3018 gpgme_strerror(err));
3019 gpgme_op_keylist_end(ctx);
3026 /* Display a menu to select a key from the array KEYS. FORCED_VALID
3027 will be set to true on return if the user did override the the
3029 static cryptkey_t *crypt_select_key (cryptkey_t * keys,
3030 address_t * p, const char *s,
3031 unsigned int app, int *forced_valid)
3034 cryptkey_t **cryptkey_table;
3037 char buf[LONG_STRING];
3039 int (*f) (const void *, const void *);
3040 int menu_to_use = 0;
3045 /* build the key table */
3047 cryptkey_table = NULL;
3048 for (k = keys; k; k = k->next) {
3049 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
3056 p_realloc(&cryptkey_table, keymax);
3059 cryptkey_table[i++] = k;
3062 if (!i && unusable) {
3063 mutt_error _("All matching keys are marked expired/revoked.");
3069 switch (PgpSortKeys & SORT_MASK) {
3071 f = crypt_compare_date;
3074 f = crypt_compare_keyid;
3077 f = crypt_compare_address;
3081 f = crypt_compare_trust;
3084 qsort (cryptkey_table, i, sizeof (cryptkey_t *), f);
3086 if (app & APPLICATION_PGP)
3087 menu_to_use = MENU_KEY_SELECT_PGP;
3088 else if (app & APPLICATION_SMIME)
3089 menu_to_use = MENU_KEY_SELECT_SMIME;
3091 menu = mutt_new_menu ();
3093 menu->make_entry = crypt_entry;
3094 menu->menu = menu_to_use;
3095 menu->data = cryptkey_table;
3100 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3101 ts = _("PGP and S/MIME keys matching");
3102 else if ((app & APPLICATION_PGP))
3103 ts = _("PGP keys matching");
3104 else if ((app & APPLICATION_SMIME))
3105 ts = _("S/MIME keys matching");
3107 ts = _("keys matching");
3110 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3112 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3116 mutt_clear_error ();
3120 switch (mutt_menuLoop (menu)) {
3122 verify_key (cryptkey_table[menu->current]);
3123 menu->redraw = REDRAW_FULL;
3127 mutt_message ("%s", cryptkey_table[menu->current]->uid);
3130 case OP_GENERIC_SELECT_ENTRY:
3131 /* FIXME make error reporting more verbose - this should be
3132 easy because gpgme provides more information */
3133 if (option (OPTPGPCHECKTRUST)) {
3134 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE ) {
3135 mutt_error(_("This key can't be used: "
3136 "expired/disabled/revoked."));
3141 if (option (OPTPGPCHECKTRUST) &&
3142 ((cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3143 || !crypt_id_is_strong (cryptkey_table[menu->current]))) {
3145 char buff[LONG_STRING];
3147 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3148 s = N_("ID is expired/disabled/revoked.");
3150 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3151 gpgme_user_id_t uid = NULL;
3156 uid = cryptkey_table[menu->current]->kobj->uids;
3157 for (j = 0; (j < cryptkey_table[menu->current]->idx) && uid;
3158 j++, uid = uid->next);
3160 val = uid->validity;
3163 case GPGME_VALIDITY_UNKNOWN:
3164 case GPGME_VALIDITY_UNDEFINED:
3165 warn_s = N_("ID has undefined validity.");
3167 case GPGME_VALIDITY_NEVER:
3168 warn_s = N_("ID is not valid.");
3170 case GPGME_VALIDITY_MARGINAL:
3171 warn_s = N_("ID is only marginally valid.");
3173 case GPGME_VALIDITY_FULL:
3174 case GPGME_VALIDITY_ULTIMATE:
3178 snprintf (buff, sizeof (buff),
3179 _("%s Do you really want to use the key?"), _(warn_s));
3181 if (mutt_yesorno (buff, 0) != 1) {
3182 mutt_clear_error ();
3189 k = cryptkey_dup(cryptkey_table[menu->current]);
3200 mutt_menuDestroy (&menu);
3201 p_delete(&cryptkey_table);
3203 set_option (OPTNEEDREDRAW);
3209 crypt_getkeybyaddr(address_t * a, int abilities, int app, int *forced_valid)
3216 int this_key_has_strong;
3217 int this_key_has_weak;
3218 int this_key_has_invalid;
3221 cryptkey_t *keys, *k;
3222 cryptkey_t *the_valid_key = NULL;
3223 cryptkey_t *matches = NULL;
3224 cryptkey_t **matches_endp = &matches;
3230 string_array_init(&hints);
3231 add_hints(&hints, a->mailbox);
3232 add_hints(&hints, a->personal);
3234 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3235 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3236 string_array_wipe(&hints);
3242 for (k = keys; k; k = k->next) {
3243 if (abilities && !(k->flags & abilities)) {
3247 this_key_has_weak = 0; /* weak but valid match */
3248 this_key_has_invalid = 0; /* invalid match */
3249 this_key_has_strong = 0; /* strong and valid match */
3250 match = 0; /* any match */
3252 r = rfc822_parse_adrlist (NULL, k->uid);
3253 for (p = r; p; p = p->next) {
3254 int validity = crypt_id_matches_addr (a, p, k);
3256 if (validity & CRYPT_KV_MATCH) /* something matches */
3259 /* is this key a strong candidate? */
3260 if ((validity & CRYPT_KV_VALID)
3261 && (validity & CRYPT_KV_STRONGID)
3262 && (validity & CRYPT_KV_ADDR)) {
3263 if (the_valid_key && the_valid_key != k)
3266 this_key_has_strong = 1;
3268 else if ((validity & CRYPT_KV_MATCH)
3269 && !(validity & CRYPT_KV_VALID))
3270 this_key_has_invalid = 1;
3271 else if ((validity & CRYPT_KV_MATCH)
3272 && (!(validity & CRYPT_KV_STRONGID)
3273 || !(validity & CRYPT_KV_ADDR)))
3274 this_key_has_weak = 1;
3276 address_list_wipe(&r);
3281 if (!this_key_has_strong && this_key_has_invalid)
3283 if (!this_key_has_strong && this_key_has_weak)
3286 *matches_endp = tmp = cryptkey_dup(k);
3287 matches_endp = &tmp->next;
3288 the_valid_key = tmp;
3291 key_list_wipe(&keys);
3294 if (the_valid_key && !multi && !weak
3295 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3297 * There was precisely one strong match on a valid ID, there
3298 * were no valid keys with weak matches, and we aren't
3299 * interested in seeing invalid keys.
3301 * Proceed without asking the user.
3303 k = cryptkey_dup(the_valid_key);
3306 * Else: Ask the user.
3308 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3310 key_list_wipe(&matches);
3320 crypt_getkeybystr(const char *p, int abilities, int app, int *forced_valid)
3323 cryptkey_t *matches = NULL;
3324 cryptkey_t **matches_endp = &matches;
3328 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3334 string_array_init(&hints);
3335 add_hints(&hints, p);
3336 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3337 string_array_wipe(&hints);
3343 for (k = keys; k; k = k->next) {
3344 const char *s = crypt_keyid(k);
3346 if (abilities && !(k->flags & abilities))
3351 if (!*p || !m_strcasecmp(p, s)
3352 || (!m_strncasecmp(p, "0x", 2) && !m_strcasecmp(p + 2, s))
3353 || m_stristr(k->uid, p))
3357 *matches_endp = tmp = cryptkey_dup(k);
3358 matches_endp = &tmp->next;
3361 key_list_wipe(&keys);
3364 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3365 key_list_wipe(&matches);
3372 /* Display TAG as a prompt to ask for a key.
3373 * ABILITIES describe the required key abilities (sign, encrypt) and APP the
3374 * type of the requested key; ether S/MIME or PGP.
3375 * Return a copy of the key or NULL if not found. */
3377 crypt_ask_for_key(const char *tag, int abilities, int app, int *forced_valid)
3384 forced_valid = &dummy;
3390 if (mutt_get_field(tag, resp, sizeof(resp), M_CLEAR) != 0)
3393 if (m_strisempty(resp))
3396 if ((key = crypt_getkeybystr(resp, abilities, app, forced_valid)))
3403 /* This routine attempts to find the keyids of the recipients of a
3404 message. It returns NULL if any of the keys can not be found. */
3405 static char *find_keys(ENVELOPE *env, unsigned int app)
3407 address_t *lst = NULL, *addr;
3408 buffer_t *keylist = buffer_new();
3411 address_t **last = &lst;
3412 *last = address_list_dup(env->to);
3413 last = address_list_last(last);
3414 *last = address_list_dup(env->cc);
3415 last = address_list_last(last);
3416 *last = address_list_dup(env->bcc);
3418 rfc822_qualify(lst, mutt_fqdn(1));
3419 address_list_uniq(lst);
3422 while ((addr = address_list_pop(&lst))) {
3424 int forced_valid = 0;
3426 cryptkey_t *key = NULL;
3428 if ((keyID = mutt_crypt_hook(addr))) {
3431 snprintf(buf, sizeof(buf), _("Use keyID = \"%s\" for %s?"), keyID,
3433 r = mutt_yesorno(buf, M_YES);
3436 address_list_wipe(&lst);
3437 address_list_wipe(&addr);
3438 buffer_delete(&keylist);
3444 /* check for e-mail address */
3445 if (strchr(keyID, '@') && (a = rfc822_parse_adrlist(NULL, keyID))) {
3446 rfc822_qualify(a, mutt_fqdn(1));
3447 address_list_wipe(&addr);
3450 key = crypt_getkeybystr(keyID, KEYFLAG_CANENCRYPT, app,
3457 key = crypt_getkeybyaddr(addr, KEYFLAG_CANENCRYPT, app, &forced_valid);
3460 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), addr->mailbox);
3461 key = crypt_ask_for_key(buf, KEYFLAG_CANENCRYPT, app,
3464 address_list_wipe(&lst);
3465 address_list_wipe(&addr);
3466 buffer_delete(&keylist);
3472 buffer_addch(keylist, ' ');
3473 buffer_addstr(keylist, "0x");
3474 buffer_addstr(keylist, crypt_fpr(key));
3476 buffer_addch(keylist, '!');
3478 key_list_wipe(&key);
3479 address_list_wipe(&addr);
3482 address_list_wipe(&lst);
3483 return buffer_unwrap(&keylist);
3486 int crypt_get_keys(HEADER *msg, char **keylist)
3488 /* Do a quick check to make sure that we can find all of the encryption
3489 * keys if the user has requested this service.
3494 if (msg->security & ENCRYPT) {
3495 if (msg->security & APPLICATION_PGP) {
3496 set_option(OPTPGPCHECKTRUST);
3497 *keylist = find_keys(msg->env, APPLICATION_PGP);
3498 unset_option(OPTPGPCHECKTRUST);
3503 if (msg->security & APPLICATION_SMIME) {
3504 *keylist = find_keys(msg->env, APPLICATION_SMIME);
3514 int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
3520 if (msg->security & APPLICATION_SMIME)
3522 if (msg->security & APPLICATION_PGP)
3526 ? mutt_multi_choice(_("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3528 : mutt_multi_choice(_("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3532 case 1: /* (e)ncrypt */
3533 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3534 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3537 case 2: /* (s)ign */
3538 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3539 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3542 case 3: /* sign (a)s */
3543 p = crypt_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN,
3544 is_smime ? APPLICATION_SMIME : APPLICATION_PGP,
3547 snprintf(buf, sizeof(buf), "0x%s", crypt_keyid(p));
3548 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs, buf);
3550 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3552 *redraw = REDRAW_FULL;
3555 case 4: /* (b)oth */
3557 msg->security = SMIMEENCRYPT | SMIMESIGN;
3559 msg->security = PGPENCRYPT | PGPSIGN;
3563 case 5: /* (p)gp or s/(m)ime */
3564 is_smime = !is_smime;
3567 case 6: /* (c)lear */
3568 return msg->security = 0;
3572 msg->security &= ~APPLICATION_PGP;
3573 msg->security |= APPLICATION_SMIME;
3575 msg->security &= ~APPLICATION_SMIME;
3576 msg->security |= APPLICATION_PGP;
3579 return msg->security;
3582 int crypt_smime_verify_sender(HEADER *h)
3584 address_t *sender = NULL;
3585 unsigned int ret = 1;
3588 h->env->from = mutt_expand_aliases(h->env->from);
3589 sender = h->env->from;
3590 } else if (h->env->sender) {
3591 h->env->sender = mutt_expand_aliases (h->env->sender);
3592 sender = h->env->sender;
3596 mutt_any_key_to_continue ("Failed to figure out sender");
3600 if (signature_key) {
3601 gpgme_key_t key = signature_key;
3602 gpgme_user_id_t uid = NULL;
3603 int sender_length = 0;
3606 sender_length = m_strlen(sender->mailbox);
3607 for (uid = key->uids; uid && ret; uid = uid->next) {
3608 uid_length = m_strlen(uid->email);
3609 if (1 && (uid->email[0] == '<')
3610 && (uid->email[uid_length - 1] == '>')
3611 && (uid_length == sender_length + 2)
3612 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3616 mutt_any_key_to_continue ("Failed to verify sender");
3620 if (signature_key) {
3621 gpgme_key_unref(signature_key);
3622 signature_key = NULL;
3627 static void crypt_invoke_import(FILE *stream, int smime)
3629 gpgme_ctx_t ctx = create_gpgme_context(smime);
3633 err = gpgme_data_new_from_stream(&data, stream);
3635 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3640 err = gpgme_op_import(ctx, data);
3642 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3643 gpgme_data_release(data);
3648 gpgme_data_release(data);
3653 static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
3656 FILE *tmpfp = tmpfile();
3658 if (tmpfp == NULL) {
3659 mutt_perror (_("Can't create temporary file"));
3666 mutt_body_handler(top, &s);
3669 crypt_invoke_import(tmpfp, 0);
3673 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3677 for (; top; top = top->next) {
3678 if (!tag || top->tagged)
3679 pgp_extract_keys_from_attachment (fp, top);
3686 void crypt_invoke_message (int type)
3688 if (type & APPLICATION_PGP) {
3689 mutt_message _("Invoking PGP...");
3691 else if (type & APPLICATION_SMIME) {
3692 mutt_message _("Invoking S/MIME...");
3696 int mutt_protect (HEADER * msg, char *keylist)
3698 BODY *pbody = NULL, *tmp_pbody = NULL;
3699 BODY *tmp_smime_pbody = NULL;
3700 BODY *tmp_pgp_pbody = NULL;
3701 int flags = msg->security;
3706 tmp_smime_pbody = msg->content;
3707 tmp_pgp_pbody = msg->content;
3709 if (msg->security & SIGN) {
3710 if (msg->security & APPLICATION_SMIME) {
3711 if (!(tmp_pbody = sign_message(msg->content, 1)))
3713 pbody = tmp_smime_pbody = tmp_pbody;
3716 if ((msg->security & APPLICATION_PGP)
3717 && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
3718 if (!(tmp_pbody = sign_message(msg->content, 0)))
3722 pbody = tmp_pgp_pbody = tmp_pbody;
3725 if ((msg->security & APPLICATION_SMIME)
3726 && (msg->security & APPLICATION_PGP)) {
3727 /* here comes the draft ;-) */
3732 if (msg->security & ENCRYPT) {
3733 if ((msg->security & APPLICATION_SMIME)) {
3734 if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
3736 /* signed ? free it! */
3739 /* free tmp_body if messages was signed AND encrypted ... */
3740 if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
3741 /* detatch and dont't delete msg->content,
3742 which tmp_smime_pbody->parts after signing. */
3743 tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
3744 msg->content->next = NULL;
3745 body_list_wipe(&tmp_smime_pbody);
3750 if ((msg->security & APPLICATION_PGP)) {
3751 if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
3754 /* did we perform a retainable signature? */
3755 if (flags != msg->security) {
3756 /* remove the outer multipart layer */
3757 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3758 /* get rid of the signature */
3759 body_list_wipe(&tmp_pgp_pbody->next);
3765 /* destroy temporary signature envelope when doing retainable
3769 if (flags != msg->security) {
3770 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3771 body_list_wipe(&tmp_pgp_pbody->next);
3777 msg->content = pbody;
3783 int crypt_query (BODY * m)
3790 if (m->type == TYPEAPPLICATION) {
3791 t |= mutt_is_application_pgp (m);
3793 t |= mutt_is_application_smime (m);
3794 if (t && m->goodsig)
3799 else if (m->type == TYPETEXT) {
3800 t |= mutt_is_application_pgp (m);
3801 if (t && m->goodsig)
3805 if (m->type == TYPEMULTIPART) {
3806 t |= mutt_is_multipart_encrypted (m);
3807 t |= mutt_is_multipart_signed (m);
3809 if (t && m->goodsig)
3813 if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
3817 u = m->parts ? ~0 : 0; /* Bits set in all parts */
3818 w = 0; /* Bits set in any part */
3820 for (p = m->parts; p; p = p->next) {
3821 v = crypt_query (p);
3825 t |= u | (w & ~GOODSIGN);
3827 if ((w & GOODSIGN) && !(u & GOODSIGN))
3835 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
3841 fseeko (s->fpin, a->hdr_offset, 0);
3842 bytes = a->length + a->offset - a->hdr_offset;
3845 if ((c = fgetc (s->fpin)) == EOF)
3853 if (c == '\n' && !hadcr)
3862 static void extract_keys_aux(FILE *fpout, HEADER *h)
3864 mutt_parse_mime_message (Context, h);
3867 if (h->security & APPLICATION_PGP) {
3868 mutt_copy_message(fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
3871 mutt_endwin (_("Trying to extract PGP keys...\n"));
3874 if (h->security & APPLICATION_SMIME) {
3875 if (h->security & ENCRYPT)
3876 mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
3877 | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
3879 mutt_copy_message(fpout, Context, h, 0, 0);
3882 mutt_message (_("Trying to extract S/MIME certificates...\n"));
3886 crypt_invoke_import(fpout, h->security & APPLICATION_SMIME);
3889 void crypt_extract_keys_from_messages(HEADER * h)
3891 FILE *tmpfp = tmpfile();
3893 mutt_error(_("Could not create temporary file"));
3899 for (i = 0; i < Context->vcount; i++) {
3900 if (!Context->hdrs[Context->v2r[i]]->tagged)
3902 extract_keys_aux(tmpfp, Context->hdrs[Context->v2r[i]]);
3905 extract_keys_aux(tmpfp, h);
3910 mutt_any_key_to_continue(NULL);
3913 static void crypt_fetch_signatures(BODY ***signatures, BODY * a, int *n)
3915 for (; a; a = a->next) {
3916 if (a->type == TYPEMULTIPART) {
3917 crypt_fetch_signatures(signatures, a->parts, n);
3920 p_realloc(signatures, *n + 6);
3922 (*signatures)[(*n)++] = a;
3927 int mutt_signed_handler(BODY *a, STATE *s)
3929 unsigned major, minor;
3931 int rc, i, goodsig = 1, sigcnt = 0;
3934 protocol = parameter_getval(a->parameter, "protocol");
3937 switch (mime_which_token(protocol, -1)) {
3938 case MIME_APPLICATION_PGP_SIGNATURE:
3939 major = TYPEAPPLICATION;
3940 minor = MIME_PGP_SIGNATURE;
3942 case MIME_APPLICATION_X_PKCS7_SIGNATURE:
3943 major = TYPEAPPLICATION;
3944 minor = MIME_X_PKCS7_SIGNATURE;
3946 case MIME_APPLICATION_PKCS7_SIGNATURE:
3947 major = TYPEAPPLICATION;
3948 minor = MIME_PKCS7_SIGNATURE;
3950 case MIME_MULTIPART_MIXED:
3951 major = TYPEMULTIPART;
3956 state_printf(s, _("[-- Error: "
3957 "Unknown multipart/signed protocol %s! --]\n\n"),
3959 return mutt_body_handler (a, s);
3962 /* consistency check */
3963 if (!(a && a->next && a->next->type == major &&
3964 mime_which_token(a->next->subtype, -1) == minor))
3966 state_attach_puts(_("[-- Error: "
3967 "Inconsistent multipart/signed structure! --]\n\n"),
3969 return mutt_body_handler (a, s);
3972 if (s->flags & M_DISPLAY) {
3975 crypt_fetch_signatures (&sigs, a->next, &sigcnt);
3977 FILE *tmpfp = tmpfile();
3980 mutt_error(_("Could not create temporary file"));
3982 crypt_write_signed(a, s, tmpfp);
3984 for (i = 0; i < sigcnt; i++) {
3985 if (sigs[i]->type == TYPEAPPLICATION) {
3988 switch ((subtype = mime_which_token(sigs[i]->subtype, -1))) {
3989 case MIME_PGP_SIGNATURE:
3990 case MIME_X_PKCS7_SIGNATURE:
3991 case MIME_PKCS7_SIGNATURE:
3992 if (crypt_verify_one(sigs[i], s, tmpfp, subtype != MIME_PGP_SIGNATURE) != 0)
4003 state_printf(s, _("[-- Warning: "
4004 "We can't verify %s/%s signatures. --]\n\n"),
4005 TYPE (sigs[i]), sigs[i]->subtype);
4009 b->goodsig = goodsig;
4010 b->badsig = !goodsig;
4012 /* Now display the signed body */
4013 state_attach_puts(_("[-- The following data is signed --]\n\n"), s);
4017 state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"),
4022 rc = mutt_body_handler (a, s);
4024 if (s->flags & M_DISPLAY && sigcnt)
4025 state_attach_puts (_("\n[-- End of signed data --]\n"), s);
4030 static int _mutt_check_traditional_pgp (HEADER * h, int *redraw)
4035 h->security |= PGP_TRADITIONAL_CHECKED;
4037 mutt_parse_mime_message (Context, h);
4038 if ((msg = mx_open_message (Context, h->msgno)) == NULL)
4040 if (crypt_pgp_check_traditional (msg->fp, h->content, 0)) {
4041 h->security = crypt_query (h->content);
4042 *redraw |= REDRAW_FULL;
4046 h->security |= PGP_TRADITIONAL_CHECKED;
4047 mx_close_message (&msg);
4051 int mutt_check_traditional_pgp (HEADER * h, int *redraw)
4056 if (h && !(h->security & PGP_TRADITIONAL_CHECKED))
4057 rv = _mutt_check_traditional_pgp (h, redraw);
4059 for (i = 0; i < Context->vcount; i++)
4060 if (Context->hdrs[Context->v2r[i]]->tagged &&
4061 !(Context->hdrs[Context->v2r[i]]->
4062 security & PGP_TRADITIONAL_CHECKED))
4064 _mutt_check_traditional_pgp (Context->hdrs[Context->v2r[i]], redraw)