+++ /dev/null
-/*
- * Copyright notice from original mutt:
- * crypt-gpgme.c - GPGME based crypto operations
- * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
- * Copyright (C) 1998,1999,2000 Thomas Roessler <roessler@guug.de>
- * Copyright (C) 2001 Thomas Roessler <roessler@guug.de>
- * Oliver Ehli <elmy@acm.org>
- * Copyright (C) 2002, 2003, 2004 g10 Code GmbH
- */
-/*
- * Copyright © 2006 Pierre Habouzit
- */
-
-#include <lib-lib/lib-lib.h>
-
-#include <gpgme.h>
-
-#include <lib-mime/mime.h>
-#include <lib-ui/curses.h>
-#include <lib-ui/enter.h>
-#include <lib-ui/menu.h>
-
-#include "crypt.h"
-
-#include "lib.h"
-#include "alias.h"
-#include "handler.h"
-#include "copy.h"
-#include "pager.h"
-#include "recvattach.h"
-#include "sort.h"
-
-/* Values used for comparing addresses. */
-#define CRYPT_KV_VALID 1
-#define CRYPT_KV_ADDR 2
-#define CRYPT_KV_STRING 4
-#define CRYPT_KV_STRONGID 8
-#define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
-
-/*
- * Type definitions.
- */
-
-struct crypt_cache {
- char *what;
- char *dflt;
- struct crypt_cache *next;
-};
-
-struct dn_array_s {
- char *key;
- char *value;
-};
-
-/* We work based on user IDs, getting from a user ID to the key is
- check and does not need any memory (gpgme uses reference counting). */
-typedef struct crypt_keyinfo {
- struct crypt_keyinfo *next;
- gpgme_key_t kobj;
- int idx; /* and the user ID at this index */
- const char *uid; /* and for convenience point to this user ID */
- unsigned int flags; /* global and per uid flags (for convenience) */
-} crypt_key_t;
-
-typedef struct crypt_entry {
- ssize_t num;
- crypt_key_t *key;
-} crypt_entry_t;
-
-
-static struct crypt_cache *id_defaults = NULL;
-static gpgme_key_t signature_key = NULL;
-
-/*
- * General helper functions.
- */
-
-/* return true when S points to a didgit or letter. */
-static int digit_or_letter (const unsigned char *s)
-{
- return ((*s >= '0' && *s <= '9')
- || (*s >= 'A' && *s <= 'Z')
- || (*s >= 'a' && *s <= 'z'));
-}
-
-
-/* Print the utf-8 encoded string BUF of length LEN bytes to stream
- FP. Convert the character set. */
-static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
-{
- char *tstr;
-
- tstr = p_dupstr(buf, len);
- mutt_convert_string (&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
- fputs (tstr, fp);
- p_delete(&tstr);
-}
-
-
-/*
- * Key management.
- */
-
-/* Return the keyID for the key K. Note that this string is valid as
- long as K is valid */
-static const char *crypt_keyid (crypt_key_t * k)
-{
- const char *s = "????????";
-
- if (k->kobj && k->kobj->subkeys) {
- s = k->kobj->subkeys->keyid;
- if ((!option (OPTPGPLONGIDS)) && (m_strlen(s) == 16))
- /* Return only the short keyID. */
- s += 8;
- }
-
- return s;
-}
-
-/* Return the hexstring fingerprint from the key K. */
-static const char *crypt_fpr (crypt_key_t * k)
-{
- const char *s = "";
-
- if (k->kobj && k->kobj->subkeys)
- s = k->kobj->subkeys->fpr;
-
- return s;
-}
-
-/* Parse FLAGS and return a statically allocated(!) string with them. */
-static char *crypt_key_abilities (int flags)
-{
- static char buff[3];
-
- if (!(flags & KEYFLAG_CANENCRYPT))
- buff[0] = '-';
- else if (flags & KEYFLAG_PREFER_SIGNING)
- buff[0] = '.';
- else
- buff[0] = 'e';
-
- if (!(flags & KEYFLAG_CANSIGN))
- buff[1] = '-';
- else if (flags & KEYFLAG_PREFER_ENCRYPTION)
- buff[1] = '.';
- else
- buff[1] = 's';
-
- buff[2] = '\0';
-
- return buff;
-}
-
-/* Parse FLAGS and return a character describing the most important flag. */
-static char crypt_flags (int flags)
-{
- if (flags & KEYFLAG_REVOKED)
- return 'R';
- else if (flags & KEYFLAG_EXPIRED)
- return 'X';
- else if (flags & KEYFLAG_DISABLED)
- return 'd';
- else if (flags & KEYFLAG_CRITICAL)
- return 'c';
- else
- return ' ';
-}
-
-/* Return a copy of KEY. */
-static crypt_key_t *crypt_copy_key (crypt_key_t *key)
-{
- crypt_key_t *k;
-
- k = p_new(crypt_key_t, 1);
- k->kobj = key->kobj;
- gpgme_key_ref (key->kobj);
- k->idx = key->idx;
- k->uid = key->uid;
- k->flags = key->flags;
-
- return k;
-}
-
-/* Release all the keys at the address of KEYLIST and set the address
- to NULL. */
-static void crypt_free_key (crypt_key_t ** keylist)
-{
- while (*keylist) {
- crypt_key_t *k = (*keylist)->next;
-
- p_delete(&k);
- *keylist = k;
- }
-}
-
-/* Return trute when key K is valid. */
-static int crypt_key_is_valid (crypt_key_t * k)
-{
- if (k->flags & KEYFLAG_CANTUSE)
- return 0;
- return 1;
-}
-
-/* Return true whe validity of KEY is sufficient. */
-static int crypt_id_is_strong (crypt_key_t * key)
-{
- gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
- gpgme_user_id_t uid = NULL;
- int is_strong = 0;
- int i = 0;
-
- if ((key->flags & KEYFLAG_ISX509))
- return 1;
-
- for (i = 0, uid = key->kobj->uids; (i < key->idx) && uid;
- i++, uid = uid->next);
- if (uid)
- val = uid->validity;
-
- switch (val) {
- case GPGME_VALIDITY_UNKNOWN:
- case GPGME_VALIDITY_UNDEFINED:
- case GPGME_VALIDITY_NEVER:
- case GPGME_VALIDITY_MARGINAL:
- is_strong = 0;
- break;
-
- case GPGME_VALIDITY_FULL:
- case GPGME_VALIDITY_ULTIMATE:
- is_strong = 1;
- break;
- }
-
- return is_strong;
-}
-
-/* Return true when the KEY is valid, i.e. not marked as unusable. */
-static int crypt_id_is_valid (crypt_key_t * key)
-{
- return !(key->flags & KEYFLAG_CANTUSE);
-}
-
-/* Return a bit vector describing how well the addresses ADDR and
- U_ADDR match and whether KEY is valid. */
-static int crypt_id_matches_addr (address_t * addr, address_t * u_addr,
- crypt_key_t * key)
-{
- int rv = 0;
-
- if (crypt_id_is_valid (key))
- rv |= CRYPT_KV_VALID;
-
- if (crypt_id_is_strong (key))
- rv |= CRYPT_KV_STRONGID;
-
- if (addr->mailbox && u_addr->mailbox
- && m_strcasecmp(addr->mailbox, u_addr->mailbox) == 0)
- rv |= CRYPT_KV_ADDR;
-
- if (addr->personal && u_addr->personal
- && m_strcasecmp(addr->personal, u_addr->personal) == 0)
- rv |= CRYPT_KV_STRING;
-
- return rv;
-}
-
-
-/*
- * GPGME convenient functions.
- */
-
-/* Create a new gpgme context and return it. With FOR_SMIME set to
- true, the protocol of the context is set to CMS. */
-static gpgme_ctx_t create_gpgme_context (int for_smime)
-{
- gpgme_error_t err;
- gpgme_ctx_t ctx;
-
- err = gpgme_new (&ctx);
- if (err) {
- mutt_error (_("error creating gpgme context: %s\n"), gpgme_strerror (err));
- sleep (2);
- mutt_exit (1);
- }
-
- if (for_smime) {
- err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
- if (err) {
- mutt_error (_("error enabling CMS protocol: %s\n"), gpgme_strerror (err));
- sleep (2);
- mutt_exit (1);
- }
- }
-
- return ctx;
-}
-
-/* Create a new gpgme data object. This is a wrapper to die on
- error. */
-static gpgme_data_t create_gpgme_data (void)
-{
- gpgme_error_t err;
- gpgme_data_t data;
-
- err = gpgme_data_new (&data);
- if (err) {
- mutt_error (_("error creating gpgme data object: %s\n"),
- gpgme_strerror (err));
- sleep (2);
- mutt_exit (1);
- }
- return data;
-}
-
-/* Create a new GPGME Data object from the mail body A. With CONVERT
- passed as true, the lines are converted to CR,LF if required.
- Return NULL on error or the gpgme_data_t object on success. */
-static gpgme_data_t body_to_data_object (BODY * a, int convert)
-{
- char tempfile[_POSIX_PATH_MAX];
- FILE *fptmp;
- int err = 0;
- gpgme_data_t data;
-
- fptmp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
- if (!fptmp) {
- mutt_perror (_("Can't create temporary file"));
- return NULL;
- }
-
- mutt_write_mime_header (a, fptmp);
- fputc ('\n', fptmp);
- mutt_write_mime_body (a, fptmp);
-
- if (convert) {
- int c, hadcr = 0;
- unsigned char buf[1];
-
- data = create_gpgme_data ();
- rewind (fptmp);
- while ((c = fgetc (fptmp)) != EOF) {
- if (c == '\r')
- hadcr = 1;
- else {
- if (c == '\n' && !hadcr) {
- buf[0] = '\r';
- gpgme_data_write (data, buf, 1);
- }
-
- hadcr = 0;
- }
- /* FIXME: This is quite suboptimal */
- buf[0] = c;
- gpgme_data_write (data, buf, 1);
- }
- gpgme_data_seek (data, 0, SEEK_SET);
- } else {
- err = gpgme_data_new_from_file (&data, tempfile, 1);
- }
- m_fclose(&fptmp);
- unlink (tempfile);
- if (err) {
- mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
- return NULL;
- }
-
- return data;
-}
-
-/* Create a GPGME data object from the stream FP but limit the object
- to LENGTH bytes starting at OFFSET bytes from the beginning of the
- file. */
-static gpgme_data_t file_to_data_object (FILE * fp, long offset, long length)
-{
- int err = 0;
- gpgme_data_t data;
-
- err = gpgme_data_new_from_filepart (&data, NULL, fp, offset, length);
- if (err) {
- mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
- return NULL;
- }
-
- return data;
-}
-
-/* Write a GPGME data object to the stream FP. */
-static int data_object_to_stream (gpgme_data_t data, FILE * fp)
-{
- int err;
- char buf[4096], *p;
- ssize_t nread;
-
- err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
- ? gpgme_error_from_errno (errno) : 0);
- if (err) {
- mutt_error (_("error rewinding data object: %s\n"), gpgme_strerror (err));
- return -1;
- }
-
- while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
- /* fixme: we are not really converting CRLF to LF but just
- skipping CR. Doing it correctly needs a more complex logic */
- for (p = buf; nread; p++, nread--) {
- if (*p != '\r')
- putc (*p, fp);
- }
-
- if (ferror (fp)) {
- mutt_perror ("[tempfile]");
- return -1;
- }
- }
- if (nread == -1) {
- mutt_error (_("error reading data object: %s\n"), strerror (errno));
- return -1;
- }
- return 0;
-}
-
-/* Copy a data object to a newly created temporay file and return that
- filename. Caller must free. With RET_FP not NULL, don't close the
- stream but return it there. */
-static char *data_object_to_tempfile (gpgme_data_t data, FILE ** ret_fp)
-{
- int err;
- char tempfile[_POSIX_PATH_MAX];
- FILE *fp;
- ssize_t nread = 0;
-
- fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
- if (!fp) {
- mutt_perror (_("Can't create temporary file"));
- return NULL;
- }
-
- err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
- ? gpgme_error_from_errno (errno) : 0);
- if (!err) {
- char buf[4096];
-
- while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
- if (fwrite (buf, nread, 1, fp) != 1) {
- mutt_perror (_("Can't create temporary file"));
- m_fclose(&fp);
- unlink (tempfile);
- return NULL;
- }
- }
- }
- if (ret_fp)
- rewind (fp);
- else
- m_fclose(&fp);
- if (nread == -1) {
- mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
- unlink (tempfile);
- m_fclose(&fp);
- return NULL;
- }
- if (ret_fp)
- *ret_fp = fp;
- return m_strdup(tempfile);
-}
-
-
-/* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
-static gpgme_error_t
-gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
- int secret)
-{
- gpgme_ctx_t listctx;
- gpgme_error_t err;
-
- if (!ctx || !r_key || !fpr)
- return gpg_error (GPG_ERR_INV_VALUE);
-
- if (strlen (fpr) < 8) /* We have at least a key ID. */
- return gpg_error (GPG_ERR_INV_VALUE);
-
- /* FIXME: We use our own context because we have to avoid the user's
- I/O callback handlers. */
- err = gpgme_new (&listctx);
- if (err)
- return err;
- gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
- err = gpgme_op_keylist_start (listctx, fpr, secret);
- if (!err)
- err = gpgme_op_keylist_next (listctx, r_key);
- gpgme_release (listctx);
- return err;
-}
-
-/* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
- The keys must be space delimited. */
-static gpgme_key_t *create_recipient_set (const char *keylist,
- gpgme_protocol_t protocol)
-{
- int err;
- const char *s;
- char buf[100];
- int i;
- gpgme_key_t *rset = NULL;
- unsigned int rset_n = 0;
- gpgme_key_t key = NULL;
- gpgme_ctx_t context = NULL;
-
- err = gpgme_new (&context);
- if (!err)
- err = gpgme_set_protocol (context, protocol);
-
- if (!err) {
- s = keylist;
- do {
- while (*s == ' ')
- s++;
- for (i = 0; *s && *s != ' ' && i < ssizeof(buf) - 1;)
- buf[i++] = *s++;
- buf[i] = 0;
- if (*buf) {
- if (i > 1 && buf[i - 1] == '!') {
- /* The user selected to override the valididy of that
- key. */
- buf[i - 1] = 0;
-
- err = gpgme_get_key2 (context, buf, &key, 0);
- if (!err)
- key->uids->validity = GPGME_VALIDITY_FULL;
- buf[i - 1] = '!';
- }
- else
- err = gpgme_get_key2 (context, buf, &key, 0);
-
- if (!err) {
- p_realloc(&rset, rset_n + 1);
- rset[rset_n++] = key;
- }
- else {
- mutt_error (_("error adding recipient `%s': %s\n"),
- buf, gpgme_strerror (err));
- p_delete(&rset);
- return NULL;
- }
- }
- } while (*s);
- }
-
- /* NULL terminate. */
- p_realloc(&rset, rset_n + 1);
- rset[rset_n++] = NULL;
-
- if (context)
- gpgme_release (context);
-
- return rset;
-}
-
-
-/* Make sure that the correct signer is set. Returns 0 on success. */
-static int set_signer (gpgme_ctx_t ctx, int for_smime)
-{
- char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
- gpgme_error_t err;
- gpgme_ctx_t listctx;
- gpgme_key_t key, key2;
-
- if (!signid || !*signid)
- return 0;
-
- listctx = create_gpgme_context (for_smime);
- err = gpgme_op_keylist_start (listctx, signid, 1);
- if (!err)
- err = gpgme_op_keylist_next (listctx, &key);
- if (err) {
- gpgme_release (listctx);
- mutt_error (_("secret key `%s' not found: %s\n"),
- signid, gpgme_strerror (err));
- return -1;
- }
- err = gpgme_op_keylist_next (listctx, &key2);
- if (!err) {
- gpgme_key_release (key);
- gpgme_key_release (key2);
- gpgme_release (listctx);
- mutt_error (_("ambiguous specification of secret key `%s'\n"), signid);
- return -1;
- }
- gpgme_op_keylist_end (listctx);
- gpgme_release (listctx);
-
- gpgme_signers_clear (ctx);
- err = gpgme_signers_add (ctx, key);
- gpgme_key_release (key);
- if (err) {
- mutt_error (_("error setting secret key `%s': %s\n"),
- signid, gpgme_strerror (err));
- return -1;
- }
- return 0;
-}
-
-
-/* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
- and return an allocated filename to a temporary file containing the
- enciphered text. With USE_SMIME set to true, the smime backend is
- used. With COMBINED_SIGNED a PGP message is signed and
- encrypted. Returns NULL in case of error */
-static char *encrypt_gpgme_object (gpgme_data_t plaintext, gpgme_key_t * rset,
- int use_smime, int combined_signed)
-{
- int err;
- gpgme_ctx_t ctx;
- gpgme_data_t ciphertext;
- char *outfile;
-
- ctx = create_gpgme_context (use_smime);
- if (!use_smime)
- gpgme_set_armor (ctx, 1);
-
- ciphertext = create_gpgme_data ();
-
- if (combined_signed) {
- if (set_signer (ctx, use_smime)) {
- gpgme_data_release (ciphertext);
- gpgme_release (ctx);
- return NULL;
- }
- err = gpgme_op_encrypt_sign (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
- plaintext, ciphertext);
- }
- else
- err = gpgme_op_encrypt (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
- plaintext, ciphertext);
- mutt_need_hard_redraw ();
- if (err) {
- mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
- gpgme_data_release (ciphertext);
- gpgme_release (ctx);
- return NULL;
- }
-
- gpgme_release (ctx);
-
- outfile = data_object_to_tempfile (ciphertext, NULL);
- gpgme_data_release (ciphertext);
- return outfile;
-}
-
-/* Find the "micalg" parameter from the last Gpgme operation on
- context CTX. It is expected that this operation was a sign
- operation. Return the algorithm name as a C string in buffer BUF
- which must have been allocated by the caller with size BUFLEN.
- Returns 0 on success or -1 in case of an error. The return string
- is truncted to BUFLEN - 1. */
-static int get_micalg (gpgme_ctx_t ctx, char *buf, ssize_t buflen)
-{
- gpgme_sign_result_t result = NULL;
- const char *algorithm_name = NULL;
-
- if (!buflen)
- return -1;
-
- *buf = 0;
- result = gpgme_op_sign_result (ctx);
- if (result) {
- algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
- if (algorithm_name) {
- m_strcpy(buf, buflen, algorithm_name);
- }
- }
-
- return *buf ? 0 : -1;
-}
-
-static void print_time (time_t t, STATE * s)
-{
- char p[STRING];
-
- setlocale (LC_TIME, "");
-#ifdef HAVE_LANGINFO_D_T_FMT
- strftime (p, sizeof (p), nl_langinfo (D_T_FMT), localtime (&t));
-#else
- strftime (p, sizeof (p), "%c", localtime (&t));
-#endif
- setlocale (LC_TIME, "C");
- state_attach_puts (p, s);
-}
-
-/* Implementation of `sign_message'. */
-
-/* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
- USE_SMIME is passed as true. Returns the new body or NULL on
- error. */
-static BODY *sign_message (BODY * a, int use_smime)
-{
- BODY *t;
- char *sigfile;
- int err = 0;
- char buf[100];
- gpgme_ctx_t ctx;
- gpgme_data_t message, signature;
-
- convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
-
- message = body_to_data_object (a, 1);
- if (!message)
- return NULL;
- signature = create_gpgme_data ();
-
- ctx = create_gpgme_context (use_smime);
- if (!use_smime)
- gpgme_set_armor (ctx, 1);
-
- if (set_signer (ctx, use_smime)) {
- gpgme_data_release (signature);
- gpgme_release (ctx);
- return NULL;
- }
-
- err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
- mutt_need_hard_redraw ();
- gpgme_data_release (message);
- if (err) {
- gpgme_data_release (signature);
- gpgme_release (ctx);
- mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
- return NULL;
- }
-
- sigfile = data_object_to_tempfile (signature, NULL);
- gpgme_data_release (signature);
- if (!sigfile) {
- gpgme_release (ctx);
- return NULL;
- }
-
- t = body_new();
- t->type = TYPEMULTIPART;
- t->subtype = m_strdup("signed");
- t->encoding = ENC7BIT;
- t->use_disp = 0;
- t->disposition = DISPINLINE;
-
- parameter_set_boundary(&t->parameter);
- parameter_setval(&t->parameter, "protocol",
- use_smime ? "application/pkcs7-signature"
- : "application/pgp-signature");
- /* Get the micalg from gpgme. Old gpgme versions don't support this
- for S/MIME so we assume sha-1 in this case. */
- if (!get_micalg (ctx, buf, sizeof buf))
- parameter_setval(&t->parameter, "micalg", buf);
- else if (use_smime)
- parameter_setval(&t->parameter, "micalg", "sha1");
- gpgme_release (ctx);
-
- t->parts = a;
- a = t;
-
- t->parts->next = body_new();
- t = t->parts->next;
- t->type = TYPEAPPLICATION;
- if (use_smime) {
- t->subtype = m_strdup("pkcs7-signature");
- parameter_setval(&t->parameter, "name", "smime.p7s");
- t->encoding = ENCBASE64;
- t->use_disp = 1;
- t->disposition = DISPATTACH;
- t->d_filename = m_strdup("smime.p7s");
- }
- else {
- t->subtype = m_strdup("pgp-signature");
- t->use_disp = 0;
- t->disposition = DISPINLINE;
- t->encoding = ENC7BIT;
- }
- t->filename = sigfile;
- t->unlink = 1; /* ok to remove this file after sending. */
-
- return a;
-}
-
-
-BODY *crypt_pgp_sign_message (BODY * a)
-{
- return sign_message (a, 0);
-}
-
-BODY *crypt_smime_sign_message (BODY * a)
-{
- return sign_message (a, 1);
-}
-
-/*
- * Implementation of `encrypt_message'.
- */
-
-/* Encrypt the mail body A to all keys given as space separated keyids
- or fingerprints in KEYLIST and return the encrypted body. */
-BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
-{
- char *outfile = NULL;
- BODY *t;
- gpgme_key_t *rset = NULL;
- gpgme_data_t plaintext;
-
- rset = create_recipient_set (keylist, GPGME_PROTOCOL_OpenPGP);
- if (!rset)
- return NULL;
-
- if (sign)
- convert_to_7bit (a);
- plaintext = body_to_data_object (a, 0);
- if (!plaintext) {
- p_delete(&rset);
- return NULL;
- }
-
- outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
- gpgme_data_release (plaintext);
- p_delete(&rset);
- if (!outfile)
- return NULL;
-
- t = body_new();
- t->type = TYPEMULTIPART;
- t->subtype = m_strdup("encrypted");
- t->encoding = ENC7BIT;
- t->use_disp = 0;
- t->disposition = DISPINLINE;
-
- parameter_set_boundary(&t->parameter);
- parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
-
- t->parts = body_new();
- t->parts->type = TYPEAPPLICATION;
- t->parts->subtype = m_strdup("pgp-encrypted");
- t->parts->encoding = ENC7BIT;
-
- t->parts->next = body_new();
- t->parts->next->type = TYPEAPPLICATION;
- t->parts->next->subtype = m_strdup("octet-stream");
- t->parts->next->encoding = ENC7BIT;
- t->parts->next->filename = outfile;
- t->parts->next->use_disp = 1;
- t->parts->next->disposition = DISPINLINE;
- t->parts->next->unlink = 1; /* delete after sending the message */
- t->parts->next->d_filename = m_strdup("msg.asc"); /* non pgp/mime
- can save */
-
- return t;
-}
-
-/*
- * Implementation of `smime_build_smime_entity'.
- */
-
-/* Encrypt the mail body A to all keys given as space separated
- fingerprints in KEYLIST and return the S/MIME encrypted body. */
-BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
-{
- char *outfile = NULL;
- BODY *t;
- gpgme_key_t *rset = NULL;
- gpgme_data_t plaintext;
-
- rset = create_recipient_set (keylist, GPGME_PROTOCOL_CMS);
- if (!rset)
- return NULL;
-
- plaintext = body_to_data_object (a, 0);
- if (!plaintext) {
- p_delete(&rset);
- return NULL;
- }
-
- outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
- gpgme_data_release (plaintext);
- p_delete(&rset);
- if (!outfile)
- return NULL;
-
- t = body_new();
- t->type = TYPEAPPLICATION;
- t->subtype = m_strdup("pkcs7-mime");
- parameter_setval(&t->parameter, "name", "smime.p7m");
- parameter_setval(&t->parameter, "smime-type", "enveloped-data");
- t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
- t->use_disp = 1;
- t->disposition = DISPATTACH;
- t->d_filename = m_strdup("smime.p7m");
- t->filename = outfile;
- t->unlink = 1; /*delete after sending the message */
- t->parts = 0;
- t->next = 0;
-
- return t;
-}
-
-
-/* Implementation of `verify_one'. */
-
-/* Display the common attributes of the signature summary SUM.
- Return 1 if there is is a severe warning.
- */
-static int show_sig_summary (unsigned long sum,
- gpgme_ctx_t ctx, gpgme_key_t key, int idx,
- STATE * s)
-{
- int severe = 0;
-
- if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
- state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
- severe = 1;
- }
-
- if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
- time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
-
- if (at) {
- state_attach_puts (_("Warning: The key used to create the "
- "signature expired at: "), s);
- print_time (at, s);
- state_attach_puts ("\n", s);
- }
- else
- state_attach_puts (_("Warning: At least one certification key "
- "has expired\n"), s);
- }
-
- if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
- gpgme_verify_result_t result;
- gpgme_signature_t sig;
- int i;
-
- result = gpgme_op_verify_result (ctx);
-
- for (sig = result->signatures, i = 0; sig && (i < idx);
- sig = sig->next, i++);
-
- state_attach_puts (_("Warning: The signature expired at: "), s);
- print_time (sig ? sig->exp_timestamp : 0, s);
- state_attach_puts ("\n", s);
- }
-
- if ((sum & GPGME_SIGSUM_KEY_MISSING))
- state_attach_puts (_("Can't verify due to a missing "
- "key or certificate\n"), s);
-
- if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
- state_attach_puts (_("The CRL is not available\n"), s);
- severe = 1;
- }
-
- if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
- state_attach_puts (_("Available CRL is too old\n"), s);
- severe = 1;
- }
-
- if ((sum & GPGME_SIGSUM_BAD_POLICY))
- state_attach_puts (_("A policy requirement was not met\n"), s);
-
- if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
- const char *t0 = NULL, *t1 = NULL;
- gpgme_verify_result_t result;
- gpgme_signature_t sig;
- int i;
-
- state_attach_puts (_("A system error occurred"), s);
-
- /* Try to figure out some more detailed system error information. */
- result = gpgme_op_verify_result (ctx);
- for (sig = result->signatures, i = 0; sig && (i < idx);
- sig = sig->next, i++);
- if (sig) {
- t0 = "";
- t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
- }
-
- if (t0 || t1) {
- state_attach_puts (": ", s);
- if (t0)
- state_attach_puts (t0, s);
- if (t1 && !(t0 && !m_strcmp(t0, t1))) {
- if (t0)
- state_attach_puts (",", s);
- state_attach_puts (t1, s);
- }
- }
- state_attach_puts ("\n", s);
- }
-
- return severe;
-}
-
-
-static void show_fingerprint (gpgme_key_t key, STATE * state)
-{
- const char *s;
- int i, is_pgp;
- char *buf, *p;
- const char *prefix = _("Fingerprint: ");
- ssize_t bufsize;
-
- if (!key)
- return;
- s = key->subkeys ? key->subkeys->fpr : NULL;
- if (!s)
- return;
- is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
-
- bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
- buf = p_new(char, bufsize);
- m_strcpy(buf, bufsize, prefix);
- p = buf + m_strlen(buf);
- if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
- for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
- *p++ = s[0];
- *p++ = s[1];
- *p++ = s[2];
- *p++ = s[3];
- *p++ = ' ';
- if (i == 4)
- *p++ = ' ';
- }
- }
- else {
- for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
- *p++ = s[0];
- *p++ = s[1];
- *p++ = is_pgp ? ' ' : ':';
- if (is_pgp && i == 7)
- *p++ = ' ';
- }
- }
-
- /* just in case print remaining odd digits */
- for (; *s; s++)
- *p++ = *s;
- *p++ = '\n';
- *p = 0;
- state_attach_puts (buf, state);
- p_delete(&buf);
-}
-
-/* Show the valididy of a key used for one signature. */
-static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
-{
- gpgme_verify_result_t result = NULL;
- gpgme_signature_t sig = NULL;
- const char *txt = NULL;
-
- result = gpgme_op_verify_result (ctx);
- if (result)
- for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
-
- switch (sig ? sig->validity : 0) {
- case GPGME_VALIDITY_UNKNOWN:
- txt = _("WARNING: We have NO indication whether "
- "the key belongs to the person named " "as shown above\n");
- break;
- case GPGME_VALIDITY_UNDEFINED:
- break;
- case GPGME_VALIDITY_NEVER:
- txt = _("WARNING: The key does NOT BELONG to "
- "the person named as shown above\n");
- break;
- case GPGME_VALIDITY_MARGINAL:
- txt = _("WARNING: It is NOT certain that the key "
- "belongs to the person named as shown above\n");
- break;
- case GPGME_VALIDITY_FULL:
- case GPGME_VALIDITY_ULTIMATE:
- txt = NULL;
- break;
- }
- if (txt)
- state_attach_puts (txt, s);
-}
-
-/* Show information about one signature. This fucntion is called with
- the context CTX of a sucessful verification operation and the
- enumerator IDX which should start at 0 and incremete for each
- call/signature.
-
- Return values are: 0 for normal procession, 1 for a bad signature,
- 2 for a signature with a warning or -1 for no more signature. */
-static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
-{
- time_t created;
- const char *fpr, *uid;
- gpgme_key_t key = NULL;
- int i, anybad = 0, anywarn = 0;
- unsigned int sum;
- gpgme_user_id_t uids = NULL;
- gpgme_verify_result_t result;
- gpgme_signature_t sig;
- gpgme_error_t err = GPG_ERR_NO_ERROR;
-
- result = gpgme_op_verify_result (ctx);
- if (result) {
- /* FIXME: this code should use a static variable and remember
- the current position in the list of signatures, IMHO.
- -moritz. */
-
- for (i = 0, sig = result->signatures; sig && (i < idx);
- i++, sig = sig->next);
- if (!sig)
- return -1; /* Signature not found. */
-
- if (signature_key) {
- gpgme_key_release (signature_key);
- signature_key = NULL;
- }
-
- created = sig->timestamp;
- fpr = sig->fpr;
- sum = sig->summary;
-
- if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
- anybad = 1;
-
- err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
- if (!err) {
- uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
- if (!signature_key)
- signature_key = key;
- }
- else {
- key = NULL; /* Old gpgme versions did not set KEY to NULL on
- error. Do it here to avoid a double free. */
- uid = "[?]";
- }
-
- if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
- else if (err) {
- state_attach_puts (_("Error getting key information: "), s);
- state_attach_puts (gpg_strerror (err), s);
- state_attach_puts ("\n", s);
- anybad = 1;
- }
- else if ((sum & GPGME_SIGSUM_GREEN)) {
- state_attach_puts (_("Good signature from: "), s);
- state_attach_puts (uid, s);
- state_attach_puts ("\n", s);
- for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
- if (i == 1)
- /* Skip primary UID. */
- continue;
- if (uids->revoked)
- continue;
- state_attach_puts (_(" aka: "), s);
- state_attach_puts (uids->uid, s);
- state_attach_puts ("\n", s);
- }
- state_attach_puts (_(" created: "), s);
- print_time (created, s);
- state_attach_puts ("\n", s);
- if (show_sig_summary (sum, ctx, key, idx, s))
- anywarn = 1;
- show_one_sig_validity (ctx, idx, s);
- }
- else if ((sum & GPGME_SIGSUM_RED)) {
- state_attach_puts (_("*BAD* signature claimed to be from: "), s);
- state_attach_puts (uid, s);
- state_attach_puts ("\n", s);
- show_sig_summary (sum, ctx, key, idx, s);
- }
- else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
- signature, so we display what a PGP user expects: The name,
- fingerprint and the key validity (which is neither fully or
- ultimate). */
- state_attach_puts (_("Good signature from: "), s);
- state_attach_puts (uid, s);
- state_attach_puts ("\n", s);
- state_attach_puts (_(" created: "), s);
- print_time (created, s);
- state_attach_puts ("\n", s);
- show_one_sig_validity (ctx, idx, s);
- show_fingerprint (key, s);
- if (show_sig_summary (sum, ctx, key, idx, s))
- anywarn = 1;
- }
- else { /* can't decide (yellow) */
-
- state_attach_puts (_("Error checking signature"), s);
- state_attach_puts ("\n", s);
- show_sig_summary (sum, ctx, key, idx, s);
- }
-
- if (key != signature_key)
- gpgme_key_release (key);
- }
-
- return anybad ? 1 : anywarn ? 2 : 0;
-}
-
-/* Do the actual verification step. With IS_SMIME set to true we
- assume S/MIME (surprise!) */
-int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
-{
- int badsig = -1;
- int anywarn = 0;
- int err;
- gpgme_ctx_t ctx;
- gpgme_data_t signature, message;
-
- signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
- if (!signature)
- return -1;
-
- /* We need to tell gpgme about the encoding because the backend can't
- auto-detect plain base-64 encoding which is used by S/MIME. */
- if (is_smime)
- gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
-
- err = gpgme_data_new_from_stream(&message, fp);
- if (err) {
- gpgme_data_release (signature);
- mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
- return -1;
- }
- ctx = create_gpgme_context (is_smime);
-
- /* Note: We don't need a current time output because GPGME avoids
- such an attack by separating the meta information from the
- data. */
- state_attach_puts (_("[-- Begin signature information --]\n"), s);
-
- err = gpgme_op_verify (ctx, signature, message, NULL);
- mutt_need_hard_redraw ();
- if (err) {
- char buf[200];
-
- snprintf (buf, sizeof (buf) - 1,
- _("Error: verification failed: %s\n"), gpgme_strerror (err));
- state_attach_puts (buf, s);
- }
- else { /* Verification succeeded, see what the result is. */
- int res, idx;
- int anybad = 0;
-
- if (signature_key) {
- gpgme_key_release (signature_key);
- signature_key = NULL;
- }
-
- for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
- if (res == 1)
- anybad = 1;
- else if (res == 2)
- anywarn = 2;
- }
- if (!anybad)
- badsig = 0;
- }
-
- if (!badsig) {
- gpgme_verify_result_t result;
- gpgme_sig_notation_t notation;
- gpgme_signature_t sig;
-
- result = gpgme_op_verify_result (ctx);
- if (result) {
- for (sig = result->signatures; sig; sig = sig->next) {
- if (sig->notations) {
- state_attach_puts ("*** Begin Notation (signature by: ", s);
- state_attach_puts (sig->fpr, s);
- state_attach_puts (") ***\n", s);
- for (notation = sig->notations; notation; notation = notation->next)
- {
- if (notation->name) {
- state_attach_puts (notation->name, s);
- state_attach_puts ("=", s);
- }
- if (notation->value) {
- state_attach_puts (notation->value, s);
- if (!(*notation->value
- && (notation->value[m_strlen(notation->value) - 1] ==
- '\n')))
- state_attach_puts ("\n", s);
- }
- }
- state_attach_puts ("*** End Notation ***\n", s);
- }
- }
- }
- }
-
- gpgme_release (ctx);
-
- state_attach_puts (_("[-- End signature information --]\n\n"), s);
-
- return badsig ? 1 : anywarn ? 2 : 0;
-}
-
-/*
- * Implementation of `decrypt_part'.
- */
-
-/* Decrypt a PGP or SMIME message (depending on the boolean flag
- IS_SMIME) with body A described further by state S. Write
- plaintext out to file FPOUT and return a new body. For PGP returns
- a flag in R_IS_SIGNED to indicate whether this is a combined
- encrypted and signed message, for S/MIME it returns true when it is
- not a encrypted but a signed message. */
-static BODY *decrypt_part (BODY * a, STATE * s, FILE * fpout, int is_smime,
- int *r_is_signed)
-{
- struct stat info;
- BODY *tattach;
- int err = 0;
- gpgme_ctx_t ctx;
- gpgme_data_t ciphertext, plaintext;
- int maybe_signed = 0;
- int anywarn = 0;
- int sig_stat = 0;
-
- if (r_is_signed)
- *r_is_signed = 0;
-
- ctx = create_gpgme_context (is_smime);
-
-restart:
- /* Make a data object from the body, create context etc. */
- ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
- if (!ciphertext)
- return NULL;
- plaintext = create_gpgme_data ();
-
- /* Do the decryption or the verification in case of the S/MIME hack. */
- if ((!is_smime) || maybe_signed) {
- if (!is_smime)
- err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
- else if (maybe_signed)
- err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
-
- {
- /* Check wether signatures have been verified. */
- gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
-
- if (verify_result->signatures)
- sig_stat = 1;
- }
- }
- else
- err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
- gpgme_data_release (ciphertext);
- if (err) {
- if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
- /* Check whether this might be a signed message despite what
- the mime header told us. Retry then. gpgsm returns the
- error information "unsupported Algorithm '?'" but gpgme
- will not store this unknown algorithm, thus we test that
- it has not been set. */
- gpgme_decrypt_result_t result;
-
- result = gpgme_op_decrypt_result (ctx);
- if (!result->unsupported_algorithm) {
- maybe_signed = 1;
- gpgme_data_release (plaintext);
- goto restart;
- }
- }
- mutt_need_hard_redraw ();
- if ((s->flags & M_DISPLAY)) {
- char buf[200];
-
- snprintf (buf, sizeof (buf) - 1,
- _("[-- Error: decryption failed: %s --]\n\n"),
- gpgme_strerror (err));
- state_attach_puts (buf, s);
- }
- gpgme_data_release (plaintext);
- gpgme_release (ctx);
- return NULL;
- }
- mutt_need_hard_redraw ();
-
- /* Read the output from GPGME, and make sure to change CRLF to LF,
- otherwise read_mime_header has a hard time parsing the message. */
- if (data_object_to_stream (plaintext, fpout)) {
- gpgme_data_release (plaintext);
- gpgme_release (ctx);
- return NULL;
- }
- gpgme_data_release (plaintext);
-
- a->is_signed_data = 0;
- if (sig_stat) {
- int res, idx;
- int anybad = 0;
-
- if (maybe_signed)
- a->is_signed_data = 1;
- if (r_is_signed)
- *r_is_signed = -1; /* A signature exists. */
-
- if ((s->flags & M_DISPLAY))
- state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
- for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
- if (res == 1)
- anybad = 1;
- else if (res == 2)
- anywarn = 1;
- }
- if (!anybad && idx && r_is_signed && *r_is_signed)
- *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
-
- if ((s->flags & M_DISPLAY))
- state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
- }
- gpgme_release (ctx);
- ctx = NULL;
-
- fflush (fpout);
- rewind (fpout);
- tattach = mutt_read_mime_header (fpout, 0);
- if (tattach) {
- /*
- * Need to set the length of this body part.
- */
- fstat (fileno (fpout), &info);
- tattach->length = info.st_size - tattach->offset;
-
- tattach->warnsig = anywarn;
-
- /* See if we need to recurse on this MIME part. */
- mutt_parse_part (fpout, tattach);
- }
-
- return tattach;
-}
-
-/* Decrypt a PGP/MIME message in FPIN and B and return a new body and
- the stream in CUR and FPOUT. Returns 0 on success. */
-int crypt_pgp_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
-{
- char tempfile[_POSIX_PATH_MAX];
- STATE s;
- BODY *first_part = b;
- int is_signed;
-
- first_part->goodsig = 0;
- first_part->warnsig = 0;
-
- if (!mutt_is_multipart_encrypted (b))
- return -1;
-
- if (!b->parts || !b->parts->next)
- return -1;
-
- b = b->parts->next;
-
- p_clear(&s, 1);
- s.fpin = fpin;
- *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
- if (!*fpout) {
- mutt_perror (_("Can't create temporary file"));
- return -1;
- }
- unlink (tempfile);
-
- *cur = decrypt_part (b, &s, *fpout, 0, &is_signed);
- rewind (*fpout);
- if (is_signed > 0)
- first_part->goodsig = 1;
-
- return *cur ? 0 : -1;
-}
-
-
-/* Decrypt a S/MIME message in FPIN and B and return a new body and
- the stream in CUR and FPOUT. Returns 0 on success. */
-int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
- BODY ** cur)
-{
- char tempfile[_POSIX_PATH_MAX];
- STATE s;
- FILE *tmpfp = NULL;
- int is_signed;
- long saved_b_offset;
- ssize_t saved_b_length;
- int saved_b_type;
-
- if (!mutt_is_application_smime (b))
- return -1;
-
- if (b->parts)
- return -1;
-
- /* Decode the body - we need to pass binary CMS to the
- backend. The backend allows for Base64 encoded data but it does
- not allow for QP which I have seen in some messages. So better
- do it here. */
- saved_b_type = b->type;
- saved_b_offset = b->offset;
- saved_b_length = b->length;
- p_clear(&s, 1);
- s.fpin = fpin;
- fseeko (s.fpin, b->offset, 0);
- tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
- if (!tmpfp) {
- mutt_perror (_("Can't create temporary file"));
- return -1;
- }
- mutt_unlink (tempfile);
-
- s.fpout = tmpfp;
- mutt_decode_attachment (b, &s);
- fflush (tmpfp);
- b->length = ftello (s.fpout);
- b->offset = 0;
- rewind (tmpfp);
-
- p_clear(&s, 1);
- s.fpin = tmpfp;
- s.fpout = 0;
- *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
- if (!*fpout) {
- mutt_perror (_("Can't create temporary file"));
- return -1;
- }
- mutt_unlink (tempfile);
-
- *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
- if (*cur)
- (*cur)->goodsig = is_signed > 0;
- b->type = saved_b_type;
- b->length = saved_b_length;
- b->offset = saved_b_offset;
- m_fclose(&tmpfp);
- rewind (*fpout);
- if (*cur && !is_signed && !(*cur)->parts
- && mutt_is_application_smime (*cur)) {
- /* Assume that this is a opaque signed s/mime message. This is
- an ugly way of doing it but we have anyway a problem with
- arbitrary encoded S/MIME messages: Only the outer part may be
- encrypted. The entire mime parsing should be revamped,
- probably by keeping the temportary files so that we don't
- need to decrypt them all the time. Inner parts of an
- encrypted part can then pint into this file and tehre won't
- never be a need to decrypt again. This needs a partial
- rewrite of the MIME engine. */
- BODY *bb = *cur;
- BODY *tmp_b;
-
- saved_b_type = bb->type;
- saved_b_offset = bb->offset;
- saved_b_length = bb->length;
- p_clear(&s, 1);
- s.fpin = *fpout;
- fseeko (s.fpin, bb->offset, 0);
- tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
- if (!tmpfp) {
- mutt_perror (_("Can't create temporary file"));
- return -1;
- }
- mutt_unlink (tempfile);
-
- s.fpout = tmpfp;
- mutt_decode_attachment (bb, &s);
- fflush (tmpfp);
- bb->length = ftello (s.fpout);
- bb->offset = 0;
- rewind (tmpfp);
- m_fclose(&*fpout);
-
- p_clear(&s, 1);
- s.fpin = tmpfp;
- s.fpout = 0;
- *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
- if (!*fpout) {
- mutt_perror (_("Can't create temporary file"));
- return -1;
- }
- mutt_unlink (tempfile);
-
- tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
- if (tmp_b)
- tmp_b->goodsig = is_signed > 0;
- bb->type = saved_b_type;
- bb->length = saved_b_length;
- bb->offset = saved_b_offset;
- m_fclose(&tmpfp);
- rewind (*fpout);
- body_list_wipe(cur);
- *cur = tmp_b;
- }
- return *cur ? 0 : -1;
-}
-
-
-/*
- * Implementation of `pgp_check_traditional'.
- */
-
-static int pgp_check_traditional_one_body (FILE * fp, BODY * b,
- int tagged_only)
-{
- char tempfile[_POSIX_PATH_MAX];
- char buf[HUGE_STRING];
- FILE *tfp;
- int tempfd;
-
- short sgn = 0;
- short enc = 0;
-
- if (b->type != TYPETEXT)
- return 0;
-
- if (tagged_only && !b->tagged)
- return 0;
-
- tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
- if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
- unlink (tempfile);
- return 0;
- }
-
- if ((tfp = fopen(tempfile, "r")) == NULL) {
- unlink (tempfile);
- return 0;
- }
-
- while (fgets (buf, sizeof (buf), tfp)) {
- if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
- if (!m_strcmp("MESSAGE-----\n", buf + 15))
- enc = 1;
- else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
- sgn = 1;
- }
- }
- m_fclose(&tfp);
- unlink (tempfile);
-
- if (!enc && !sgn)
- return 0;
-
- /* fix the content type */
-
- parameter_setval(&b->parameter, "format", "fixed");
- parameter_setval(&b->parameter, "x-action",
- enc ? "pgp-encrypted" : "pgp-signed");
- return 1;
-}
-
-int crypt_pgp_check_traditional (FILE * fp, BODY * b, int tagged_only)
-{
- int rv = 0;
- int r;
-
- for (; b; b = b->next) {
- if (is_multipart (b))
- rv = (crypt_pgp_check_traditional (fp, b->parts, tagged_only) || rv);
- else if (b->type == TYPETEXT) {
- if ((r = mutt_is_application_pgp (b)))
- rv = (rv || r);
- else
- rv = (pgp_check_traditional_one_body (fp, b, tagged_only) || rv);
- }
- }
- return rv;
-}
-
-
-/* Implementation of `application_handler'. */
-
-/*
- Copy a clearsigned message, and strip the signature and PGP's
- dash-escaping.
-
- XXX - charset handling: We assume that it is safe to do
- character set decoding first, dash decoding second here, while
- we do it the other way around in the main handler.
-
- (Note that we aren't worse than Outlook & Cie in this, and also
- note that we can successfully handle anything produced by any
- existing versions of mutt.) */
-
-static void copy_clearsigned (gpgme_data_t data, STATE * s, char *charset)
-{
- char buf[HUGE_STRING];
- short complete, armor_header;
- fgetconv_t *fc;
- char *fname;
- FILE *fp;
-
- fname = data_object_to_tempfile (data, &fp);
- if (!fname)
- return;
- unlink (fname);
- p_delete(&fname);
-
- fc = fgetconv_open (fp, charset, MCharset.charset, M_ICONV_HOOK_FROM);
-
- for (complete = 1, armor_header = 1;
- fgetconvs (buf, sizeof (buf), fc) != NULL;
- complete = strchr (buf, '\n') != NULL) {
- if (!complete) {
- if (!armor_header)
- state_puts (buf, s);
- continue;
- }
-
- if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
- break;
-
- if (armor_header) {
- if (buf[0] == '\n')
- armor_header = 0;
- continue;
- }
-
- if (s->prefix)
- state_puts (s->prefix, s);
-
- if (buf[0] == '-' && buf[1] == ' ')
- state_puts (buf + 2, s);
- else
- state_puts (buf, s);
- }
-
- fgetconv_close (&fc);
- m_fclose(&fp);
-}
-
-
-/* Support for classic_application/pgp */
-int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
-{
- int needpass = -1, pgp_keyblock = 0;
- int clearsign = 0;
- long start_pos = 0;
- long bytes;
- off_t last_pos, offset;
- char buf[HUGE_STRING];
- FILE *pgpout = NULL;
-
- gpgme_error_t err = 0;
- gpgme_data_t armored_data = NULL;
-
- short maybe_goodsig = 1;
- short have_any_sigs = 0;
-
- char body_charset[STRING]; /* Only used for clearsigned messages. */
-
- /* For clearsigned messages we won't be able to get a character set
- but we know that this may only be text thus we assume Latin-1
- here. */
- if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
- m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
-
- fseeko (s->fpin, m->offset, 0);
- last_pos = m->offset;
-
- for (bytes = m->length; bytes > 0;) {
- if (fgets (buf, sizeof (buf), s->fpin) == NULL)
- break;
-
- offset = ftello (s->fpin);
- bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
- last_pos = offset;
-
- if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
- clearsign = 0;
- start_pos = last_pos;
-
- if (!m_strcmp("MESSAGE-----\n", buf + 15))
- needpass = 1;
- else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
- clearsign = 1;
- needpass = 0;
- }
- else if (!option (OPTDONTHANDLEPGPKEYS) &&
- !m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
- needpass = 0;
- pgp_keyblock = 1;
- }
- else {
- /* XXX - we may wish to recode here */
- if (s->prefix)
- state_puts (s->prefix, s);
- state_puts (buf, s);
- continue;
- }
-
- have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
-
- /* Copy PGP material to an data container */
- armored_data = create_gpgme_data ();
- gpgme_data_write (armored_data, buf, m_strlen(buf));
- while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
- offset = ftello (s->fpin);
- bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
- last_pos = offset;
-
- gpgme_data_write (armored_data, buf, m_strlen(buf));
-
- if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
- || (!needpass
- && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
- || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
- buf))))
- break;
- }
-
- /* Invoke PGP if needed */
- if (!clearsign || (s->flags & M_VERIFY)) {
- unsigned int sig_stat = 0;
- gpgme_data_t plaintext;
- gpgme_ctx_t ctx;
-
- plaintext = create_gpgme_data ();
- ctx = create_gpgme_context (0);
-
- if (clearsign)
- err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
- else {
- err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
- if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
- /* Decrypt verify can't handle signed only messages. */
- err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
- ? gpgme_error_from_errno (errno) : 0;
- /* Must release plaintext so that we supply an
- uninitialized object. */
- gpgme_data_release (plaintext);
- plaintext = create_gpgme_data ();
- err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
- }
- }
-
- if (err) {
- char errbuf[200];
-
- snprintf (errbuf, sizeof (errbuf) - 1,
- _("Error: decryption/verification failed: %s\n"),
- gpgme_strerror (err));
- state_attach_puts (errbuf, s);
- }
- else { /* Decryption/Verification succeeded */
- char *tmpfname;
-
- {
- /* Check wether signatures have been verified. */
- gpgme_verify_result_t verify_result;
-
- verify_result = gpgme_op_verify_result (ctx);
- if (verify_result->signatures)
- sig_stat = 1;
- }
-
- have_any_sigs = 0;
- maybe_goodsig = 0;
- if ((s->flags & M_DISPLAY) && sig_stat) {
- int res, idx;
- int anybad = 0;
- int anywarn = 0;
-
- state_attach_puts (_("[-- Begin signature "
- "information --]\n"), s);
- have_any_sigs = 1;
- for (idx = 0;
- (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
- if (res == 1)
- anybad = 1;
- else if (res == 2)
- anywarn = 1;
- }
- if (!anybad && idx)
- maybe_goodsig = 1;
-
- state_attach_puts (_("[-- End signature "
- "information --]\n\n"), s);
- }
-
- tmpfname = data_object_to_tempfile (plaintext, &pgpout);
- if (!tmpfname) {
- pgpout = NULL;
- state_attach_puts (_("Error: copy data failed\n"), s);
- }
- else {
- unlink (tmpfname);
- p_delete(&tmpfname);
- }
- }
- gpgme_release (ctx);
- }
-
- /*
- * Now, copy cleartext to the screen. NOTE - we expect that PGP
- * outputs utf-8 cleartext. This may not always be true, but it
- * seems to be a reasonable guess.
- */
-
- if (s->flags & M_DISPLAY) {
- if (needpass)
- state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
- else if (pgp_keyblock)
- state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
- else
- state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
- }
-
- if (clearsign) {
- copy_clearsigned (armored_data, s, body_charset);
- }
- else if (pgpout) {
- fgetconv_t *fc;
- int c;
-
- rewind (pgpout);
- fc = fgetconv_open (pgpout, "utf-8", MCharset.charset, 0);
- while ((c = fgetconv (fc)) != EOF) {
- state_putc (c, s);
- if (c == '\n' && s->prefix)
- state_puts (s->prefix, s);
- }
- fgetconv_close (&fc);
- }
-
- if (s->flags & M_DISPLAY) {
- state_putc ('\n', s);
- if (needpass)
- state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
- else if (pgp_keyblock)
- state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
- else
- state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
- }
-
- if (pgpout) {
- m_fclose(&pgpout);
- }
- }
- else {
- /* XXX - we may wish to recode here */
- if (s->prefix)
- state_puts (s->prefix, s);
- state_puts (buf, s);
- }
- }
-
- m->goodsig = (maybe_goodsig && have_any_sigs);
-
- if (needpass == -1) {
- state_attach_puts (_("[-- Error: could not find beginning"
- " of PGP message! --]\n\n"), s);
- return (-1);
- }
- return (err);
-}
-
-/* Implementation of `encrypted_handler'. */
-
-/* MIME handler for pgp/mime encrypted messages. */
-int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
-{
- char tempfile[_POSIX_PATH_MAX];
- FILE *fpout;
- BODY *tattach;
- BODY *orig_body = a;
- int is_signed;
- int rc = 0;
-
- a = a->parts;
- if (!a || a->type != TYPEAPPLICATION || !a->subtype
- || ascii_strcasecmp ("pgp-encrypted", a->subtype)
- || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
- || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
- if (s->flags & M_DISPLAY)
- state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
- s);
- return (-1);
- }
-
- /* Move forward to the application/pgp-encrypted body. */
- a = a->next;
-
- fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
- if (!fpout) {
- if (s->flags & M_DISPLAY)
- state_attach_puts (_("[-- Error: could not create temporary file! "
- "--]\n"), s);
- return (-1);
- }
-
- tattach = decrypt_part (a, s, fpout, 0, &is_signed);
- if (tattach) {
- tattach->goodsig = is_signed > 0;
-
- if (s->flags & M_DISPLAY)
- state_attach_puts (is_signed ?
- _
- ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
- _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
-
- {
- FILE *savefp = s->fpin;
-
- s->fpin = fpout;
- rc = mutt_body_handler (tattach, s);
- s->fpin = savefp;
- }
-
- /*
- * if a multipart/signed is the _only_ sub-part of a
- * multipart/encrypted, cache signature verification
- * status.
- */
- if (mutt_is_multipart_signed (tattach) && !tattach->next)
- orig_body->goodsig |= tattach->goodsig;
-
- if (s->flags & M_DISPLAY) {
- state_puts ("\n", s);
- state_attach_puts (is_signed ?
- _
- ("[-- End of PGP/MIME signed and encrypted data --]\n")
- : _("[-- End of PGP/MIME encrypted data --]\n"), s);
- }
-
- body_list_wipe(&tattach);
- }
-
- m_fclose(&fpout);
- mutt_unlink (tempfile);
- return (rc);
-}
-
-/* Support for application/smime */
-int crypt_smime_application_smime_handler (BODY * a, STATE * s)
-{
- char tempfile[_POSIX_PATH_MAX];
- FILE *fpout;
- BODY *tattach;
- int is_signed;
- int rc = 0;
-
- a->warnsig = 0;
- fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
- if (!fpout) {
- if (s->flags & M_DISPLAY)
- state_attach_puts (_("[-- Error: could not create temporary file! "
- "--]\n"), s);
- return (-1);
- }
-
- tattach = decrypt_part (a, s, fpout, 1, &is_signed);
- if (tattach) {
- tattach->goodsig = is_signed > 0;
-
- if (s->flags & M_DISPLAY)
- state_attach_puts (is_signed ?
- _("[-- The following data is S/MIME signed --]\n\n") :
- _("[-- The following data is S/MIME encrypted --]\n\n"), s);
-
- {
- FILE *savefp = s->fpin;
-
- s->fpin = fpout;
- rc = mutt_body_handler (tattach, s);
- s->fpin = savefp;
- }
-
- /*
- * if a multipart/signed is the _only_ sub-part of a
- * multipart/encrypted, cache signature verification
- * status.
- */
- if (mutt_is_multipart_signed (tattach) && !tattach->next) {
- if (!(a->goodsig = tattach->goodsig))
- a->warnsig = tattach->warnsig;
- }
- else if (tattach->goodsig) {
- a->goodsig = 1;
- a->warnsig = tattach->warnsig;
- }
-
- if (s->flags & M_DISPLAY) {
- state_puts ("\n", s);
- state_attach_puts (is_signed ?
- _("[-- End of S/MIME signed data --]\n") :
- _("[-- End of S/MIME encrypted data --]\n"), s);
- }
-
- body_list_wipe(&tattach);
- }
-
- m_fclose(&fpout);
- mutt_unlink (tempfile);
- return (rc);
-}
-
-
-/*
- * Format an entry on the CRYPT key selection menu.
- *
- * %n number
- * %k key id %K key id of the principal key
- * %u user id
- * %a algorithm %A algorithm of the princ. key
- * %l length %L length of the princ. key
- * %f flags %F flags of the princ. key
- * %c capabilities %C capabilities of the princ. key
- * %t trust/validity of the key-uid association
- * %p protocol
- * %[...] date of key using strftime(3)
- */
-
-static const char *
-crypt_entry_fmt (char *dest, ssize_t destlen, char op,
- const char *src, const char *prefix,
- const char *ifstr, const char *elstr,
- anytype data, format_flag flags)
-{
- char fmt[16];
- crypt_entry_t *entry;
- crypt_key_t *key;
- int kflags = 0;
- int optional = (flags & M_FORMAT_OPTIONAL);
- const char *s = NULL;
- unsigned long val;
-
- entry = data.ptr;
- key = entry->key;
-
-/* if (isupper ((unsigned char) op)) */
-/* key = pkey; */
-
- kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
- | uid->flags */ );
-
- switch (ascii_tolower (op)) {
- case '[':
- {
- const char *cp;
- char buf2[STRING], *p;
- int do_locales;
- struct tm *tm;
- ssize_t len;
-
- p = dest;
-
- cp = src;
- if (*cp == '!') {
- do_locales = 0;
- cp++;
- }
- else
- do_locales = 1;
-
- len = destlen - 1;
- while (len > 0 && *cp != ']') {
- if (*cp == '%') {
- cp++;
- if (len >= 2) {
- *p++ = '%';
- *p++ = *cp;
- len -= 2;
- }
- else
- break; /* not enough space */
- cp++;
- }
- else {
- *p++ = *cp++;
- len--;
- }
- }
- *p = 0;
-
- if (do_locales && Locale)
- setlocale (LC_TIME, Locale);
-
- {
- time_t tt = 0;
-
- if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
- tt = key->kobj->subkeys->timestamp;
-
- tm = localtime (&tt);
- }
- strftime (buf2, sizeof (buf2), dest, tm);
-
- if (do_locales)
- setlocale (LC_TIME, "C");
-
- snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
- snprintf (dest, destlen, fmt, buf2);
- if (len > 0)
- src = cp + 1;
- }
- break;
- case 'n':
- if (!optional) {
- snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
- snprintf (dest, destlen, fmt, entry->num);
- }
- break;
- case 'k':
- if (!optional) {
- /* fixme: we need a way to distinguish between main and subkeys.
- Store the idx in entry? */
- snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
- snprintf (dest, destlen, fmt, crypt_keyid (key));
- }
- break;
- case 'u':
- if (!optional) {
- snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
- snprintf (dest, destlen, fmt, key->uid);
- }
- break;
- case 'a':
- if (!optional) {
- snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
- if (key->kobj->subkeys)
- s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
- else
- s = "?";
- snprintf (dest, destlen, fmt, s);
- }
- break;
- case 'l':
- if (!optional) {
- snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
- if (key->kobj->subkeys)
- val = key->kobj->subkeys->length;
- else
- val = 0;
- snprintf (dest, destlen, fmt, val);
- }
- break;
- case 'f':
- if (!optional) {
- snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
- snprintf (dest, destlen, fmt, crypt_flags (kflags));
- }
- else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
- optional = 0;
- break;
- case 'c':
- if (!optional) {
- snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
- snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
- }
- else if (!(kflags & (KEYFLAG_ABILITIES)))
- optional = 0;
- break;
- case 't':
- if ((kflags & KEYFLAG_ISX509))
- s = "x";
- else {
- gpgme_user_id_t uid = NULL;
- int i = 0;
-
- for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
- i++, uid = uid->next);
- if (uid)
- switch (uid->validity) {
- case GPGME_VALIDITY_UNDEFINED:
- s = "q";
- break;
- case GPGME_VALIDITY_NEVER:
- s = "n";
- break;
- case GPGME_VALIDITY_MARGINAL:
- s = "m";
- break;
- case GPGME_VALIDITY_FULL:
- s = "f";
- break;
- case GPGME_VALIDITY_ULTIMATE:
- s = "u";
- break;
- case GPGME_VALIDITY_UNKNOWN:
- default:
- s = "?";
- break;
- }
- }
- snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
- snprintf (dest, destlen, fmt, s ? *s : 'B');
- break;
- case 'p':
- snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
- snprintf (dest, destlen, fmt,
- gpgme_get_protocol_name (key->kobj->protocol));
- break;
-
- default:
- *dest = '\0';
- }
-
- if (flags & M_FORMAT_OPTIONAL)
- m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
- mutt_attach_fmt, data, 0);
- return src;
-}
-
-/* Used by the display fucntion to format a line. */
-static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
-{
- crypt_key_t **key_table = (crypt_key_t **) menu->data;
- crypt_entry_t entry;
-
- entry.key = key_table[num];
- entry.num = num + 1;
-
- m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
- option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
-}
-
-/* Compare two addresses and the keyid to be used for sorting. */
-static int _crypt_compare_address (const void *a, const void *b)
-{
- crypt_key_t **s = (crypt_key_t **) a;
- crypt_key_t **t = (crypt_key_t **) b;
- int r;
-
- if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
- return r > 0;
- else
- return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
-}
-
-static int crypt_compare_address (const void *a, const void *b)
-{
- return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
- : _crypt_compare_address (a, b));
-}
-
-
-/* Compare two key IDs and the addresses to be used for sorting. */
-static int _crypt_compare_keyid (const void *a, const void *b)
-{
- crypt_key_t **s = (crypt_key_t **) a;
- crypt_key_t **t = (crypt_key_t **) b;
- int r;
-
- if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
- return r > 0;
- else
- return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
-}
-
-static int crypt_compare_keyid (const void *a, const void *b)
-{
- return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
- : _crypt_compare_keyid (a, b));
-}
-
-/* Compare 2 creation dates and the addresses. For sorting. */
-static int _crypt_compare_date (const void *a, const void *b)
-{
- crypt_key_t **s = (crypt_key_t **) a;
- crypt_key_t **t = (crypt_key_t **) b;
- unsigned long ts = 0, tt = 0;
-
- if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
- ts = (*s)->kobj->subkeys->timestamp;
- if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
- tt = (*t)->kobj->subkeys->timestamp;
-
- if (ts > tt)
- return 1;
- if (ts < tt)
- return 0;
-
- return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
-}
-
-static int crypt_compare_date (const void *a, const void *b)
-{
- return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
- : _crypt_compare_date (a, b));
-}
-
-/* Compare two trust values, the key length, the creation dates. the
- addresses and the key IDs. For sorting. */
-static int _crypt_compare_trust (const void *a, const void *b)
-{
- crypt_key_t **s = (crypt_key_t **) a;
- crypt_key_t **t = (crypt_key_t **) b;
- unsigned long ts = 0, tt = 0;
- int r;
-
- if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
- - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
- return r > 0;
-
- if ((*s)->kobj->uids)
- ts = (*s)->kobj->uids->validity;
- if ((*t)->kobj->uids)
- tt = (*t)->kobj->uids->validity;
- if ((r = (tt - ts)))
- return r < 0;
-
- if ((*s)->kobj->subkeys)
- ts = (*s)->kobj->subkeys->length;
- if ((*t)->kobj->subkeys)
- tt = (*t)->kobj->subkeys->length;
- if (ts != tt)
- return ts > tt;
-
- if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
- ts = (*s)->kobj->subkeys->timestamp;
- if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
- tt = (*t)->kobj->subkeys->timestamp;
- if (ts > tt)
- return 1;
- if (ts < tt)
- return 0;
-
- if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
- return r > 0;
- return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
-}
-
-static int crypt_compare_trust (const void *a, const void *b)
-{
- return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
- : _crypt_compare_trust (a, b));
-}
-
-/* Print the X.500 Distinguished Name part KEY from the array of parts
- DN to FP. */
-static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
-{
- int any = 0;
-
- for (; dn->key; dn++) {
- if (!m_strcmp(dn->key, key)) {
- if (any)
- fputs (" + ", fp);
- print_utf8 (fp, dn->value, m_strlen(dn->value));
- any = 1;
- }
- }
- return any;
-}
-
-/* Print all parts of a DN in a standard sequence. */
-static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
-{
- const char *stdpart[] = {
- "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
- };
- int any = 0, any2 = 0, i;
-
- for (i = 0; stdpart[i]; i++) {
- if (any)
- fputs (", ", fp);
- any = print_dn_part (fp, dn, stdpart[i]);
- }
- /* now print the rest without any specific ordering */
- for (; dn->key; dn++) {
- for (i = 0; stdpart[i]; i++) {
- if (!m_strcmp(dn->key, stdpart[i]))
- break;
- }
- if (!stdpart[i]) {
- if (any)
- fputs (", ", fp);
- if (!any2)
- fputs ("(", fp);
- any = print_dn_part (fp, dn, dn->key);
- any2 = 1;
- }
- }
- if (any2)
- fputs (")", fp);
-}
-
-
-/* Parse an RDN; this is a helper to parse_dn(). */
-static const unsigned char *parse_dn_part (struct dn_array_s *array,
- const unsigned char *string)
-{
- const unsigned char *s, *s1;
- ssize_t n;
- unsigned char *p;
-
- /* parse attributeType */
- for (s = string + 1; *s && *s != '='; s++);
- if (!*s)
- return NULL; /* error */
- n = s - string;
- if (!n)
- return NULL; /* empty key */
- array->key = p_dupstr(string, n );
- p = (unsigned char *) array->key;
- string = s + 1;
-
- if (*string == '#') { /* hexstring */
- string++;
- for (s = string; hexval(*s) >= 0; s++)
- s++;
- n = s - string;
- if (!n || (n & 1))
- return NULL; /* empty or odd number of digits */
- n /= 2;
- p = p_new(unsigned char, n + 1);
- array->value = (char *) p;
- for (s1 = string; n; s1 += 2, n--)
- *p++ = (hexval(*s1) << 8) | hexval(*s1);
- *p = 0;
- }
- else { /* regular v3 quoted string */
- for (n = 0, s = string; *s; s++) {
- if (*s == '\\') { /* pair */
- s++;
- if (*s == ',' || *s == '=' || *s == '+'
- || *s == '<' || *s == '>' || *s == '#' || *s == ';'
- || *s == '\\' || *s == '\"' || *s == ' ')
- n++;
- else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
- s++;
- n++;
- }
- else
- return NULL; /* invalid escape sequence */
- }
- else if (*s == '\"')
- return NULL; /* invalid encoding */
- else if (*s == ',' || *s == '=' || *s == '+'
- || *s == '<' || *s == '>' || *s == '#' || *s == ';')
- break;
- else
- n++;
- }
-
- p = p_new(unsigned char, n + 1);
- array->value = (char *) p;
- for (s = string; n; s++, n--) {
- if (*s == '\\') {
- s++;
- if (hexval(*s) >= 0) {
- *p++ = (hexval(*s) << 8) | hexval(*s + 1);
- s++;
- }
- else
- *p++ = *s;
- }
- else
- *p++ = *s;
- }
- *p = 0;
- }
- return s;
-}
-
-
-/* Parse a DN and return an array-ized one. This is not a validating
- parser and it does not support any old-stylish syntax; gpgme is
- expected to return only rfc2253 compatible strings. */
-static struct dn_array_s *parse_dn (const unsigned char *string)
-{
- struct dn_array_s *array;
- ssize_t arrayidx, arraysize;
- int i;
-
- arraysize = 7; /* C,ST,L,O,OU,CN,email */
- array = p_new(struct dn_array_s, arraysize + 1);
- arrayidx = 0;
- while (*string) {
- while (*string == ' ')
- string++;
- if (!*string)
- break; /* ready */
- if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
- struct dn_array_s *a2;
-
- arraysize += 5;
- a2 = p_new(struct dn_array_s, arraysize + 1);
- for (i = 0; i < arrayidx; i++) {
- a2[i].key = array[i].key;
- a2[i].value = array[i].value;
- }
- p_delete(&array);
- array = a2;
- }
- array[arrayidx].key = NULL;
- array[arrayidx].value = NULL;
- string = parse_dn_part (array + arrayidx, string);
- arrayidx++;
- if (!string)
- goto failure;
- while (*string == ' ')
- string++;
- if (*string && *string != ',' && *string != ';' && *string != '+')
- goto failure; /* invalid delimiter */
- if (*string)
- string++;
- }
- array[arrayidx].key = NULL;
- array[arrayidx].value = NULL;
- return array;
-
-failure:
- for (i = 0; i < arrayidx; i++) {
- p_delete(&array[i].key);
- p_delete(&array[i].value);
- }
- p_delete(&array);
- return NULL;
-}
-
-
-/* Print a nice representation of the USERID and make sure it is
- displayed in a proper way, which does mean to reorder some parts
- for S/MIME's DNs. USERID is a string as returned by the gpgme key
- functions. It is utf-8 encoded. */
-static void parse_and_print_user_id (FILE * fp, const char *userid)
-{
- const char *s;
- int i;
-
- if (*userid == '<') {
- s = strchr (userid + 1, '>');
- if (s)
- print_utf8 (fp, userid + 1, s - userid - 1);
- }
- else if (*userid == '(')
- fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
- else if (!digit_or_letter ((const unsigned char *) userid))
- fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
- else {
- struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
-
- if (!dn)
- fputs (_("[Can't display this user ID (invalid DN)]"), fp);
- else {
- print_dn_parts (fp, dn);
- for (i = 0; dn[i].key; i++) {
- p_delete(&dn[i].key);
- p_delete(&dn[i].value);
- }
- p_delete(&dn);
- }
- }
-}
-
-typedef enum {
- KEY_CAP_CAN_ENCRYPT,
- KEY_CAP_CAN_SIGN,
- KEY_CAP_CAN_CERTIFY
-} key_cap_t;
-
-static unsigned int key_check_cap (gpgme_key_t key, key_cap_t cap)
-{
- gpgme_subkey_t subkey = NULL;
- unsigned int ret = 0;
-
- switch (cap) {
- case KEY_CAP_CAN_ENCRYPT:
- if (!(ret = key->can_encrypt))
- for (subkey = key->subkeys; subkey; subkey = subkey->next)
- if ((ret = subkey->can_encrypt))
- break;
- break;
- case KEY_CAP_CAN_SIGN:
- if (!(ret = key->can_sign))
- for (subkey = key->subkeys; subkey; subkey = subkey->next)
- if ((ret = subkey->can_sign))
- break;
- break;
- case KEY_CAP_CAN_CERTIFY:
- if (!(ret = key->can_certify))
- for (subkey = key->subkeys; subkey; subkey = subkey->next)
- if ((ret = subkey->can_certify))
- break;
- break;
- }
-
- return ret;
-}
-
-
-/* Print verbose information about a key or certificate to FP. */
-static void print_key_info (gpgme_key_t key, FILE * fp)
-{
- int idx;
- const char *s = NULL, *s2 = NULL;
- time_t tt = 0;
- struct tm *tm;
- char shortbuf[STRING];
- unsigned long aval = 0;
- const char *delim;
- int is_pgp = 0;
- int i;
- gpgme_user_id_t uid = NULL;
-
- if (Locale)
- setlocale (LC_TIME, Locale);
-
- is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
-
- for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
- if (uid->revoked)
- continue;
-
- s = uid->uid;
- fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
-
- if (uid->invalid) {
- fputs (_("[Invalid]"), fp);
- putc (' ', fp);
- }
- if (is_pgp)
- print_utf8 (fp, s, m_strlen(s));
- else
- parse_and_print_user_id (fp, s);
- putc ('\n', fp);
- }
-
- if (key->subkeys && (key->subkeys->timestamp > 0)) {
- tt = key->subkeys->timestamp;
-
- tm = localtime (&tt);
-#ifdef HAVE_LANGINFO_D_T_FMT
- strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
-#else
- strftime (shortbuf, sizeof shortbuf, "%c", tm);
-#endif
- fprintf (fp, _("Valid From : %s\n"), shortbuf);
- }
-
- if (key->subkeys && (key->subkeys->expires > 0)) {
- tt = key->subkeys->expires;
-
- tm = localtime (&tt);
-#ifdef HAVE_LANGINFO_D_T_FMT
- strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
-#else
- strftime (shortbuf, sizeof shortbuf, "%c", tm);
-#endif
- fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
- }
-
- if (key->subkeys)
- s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
- else
- s = "?";
-
- s2 = is_pgp ? "PGP" : "X.509";
-
- if (key->subkeys)
- aval = key->subkeys->length;
-
- fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
-
- fprintf (fp, _("Key Usage .: "));
- delim = "";
-
- if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
- fprintf (fp, "%s%s", delim, _("encryption"));
- delim = _(", ");
- }
- if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
- fprintf (fp, "%s%s", delim, _("signing"));
- delim = _(", ");
- }
- if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
- fprintf (fp, "%s%s", delim, _("certification"));
- delim = _(", ");
- }
- putc ('\n', fp);
-
- if (key->subkeys) {
- s = key->subkeys->fpr;
- fputs (_("Fingerprint: "), fp);
- if (is_pgp && m_strlen(s) == 40) {
- for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
- putc (*s, fp);
- putc (s[1], fp);
- putc (s[2], fp);
- putc (s[3], fp);
- putc (is_pgp ? ' ' : ':', fp);
- if (is_pgp && i == 4)
- putc (' ', fp);
- }
- }
- else {
- for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
- putc (*s, fp);
- putc (s[1], fp);
- putc (is_pgp ? ' ' : ':', fp);
- if (is_pgp && i == 7)
- putc (' ', fp);
- }
- }
- fprintf (fp, "%s\n", s);
- }
-
- if (key->issuer_serial) {
- s = key->issuer_serial;
- if (s)
- fprintf (fp, _("Serial-No .: 0x%s\n"), s);
- }
-
- if (key->issuer_name) {
- s = key->issuer_name;
- if (s) {
- fprintf (fp, _("Issued By .: "));
- parse_and_print_user_id (fp, s);
- putc ('\n', fp);
- }
- }
-
- /* For PGP we list all subkeys. */
- if (is_pgp) {
- gpgme_subkey_t subkey = NULL;
-
- for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
- s = subkey->keyid;
-
- putc ('\n', fp);
- if (m_strlen(s) == 16)
- s += 8; /* display only the short keyID */
- fprintf (fp, _("Subkey ....: 0x%s"), s);
- if (subkey->revoked) {
- putc (' ', fp);
- fputs (_("[Revoked]"), fp);
- }
- if (subkey->invalid) {
- putc (' ', fp);
- fputs (_("[Invalid]"), fp);
- }
- if (subkey->expired) {
- putc (' ', fp);
- fputs (_("[Expired]"), fp);
- }
- if (subkey->disabled) {
- putc (' ', fp);
- fputs (_("[Disabled]"), fp);
- }
- putc ('\n', fp);
-
- if (subkey->timestamp > 0) {
- tt = subkey->timestamp;
-
- tm = localtime (&tt);
-#ifdef HAVE_LANGINFO_D_T_FMT
- strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
-#else
- strftime (shortbuf, sizeof shortbuf, "%c", tm);
-#endif
- fprintf (fp, _("Valid From : %s\n"), shortbuf);
- }
-
- if (subkey->expires > 0) {
- tt = subkey->expires;
-
- tm = localtime (&tt);
-#ifdef HAVE_LANGINFO_D_T_FMT
- strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
-#else
- strftime (shortbuf, sizeof shortbuf, "%c", tm);
-#endif
- fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
- }
-
- if (subkey)
- s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
- else
- s = "?";
-
- if (subkey)
- aval = subkey->length;
- else
- aval = 0;
-
- fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
-
- fprintf (fp, _("Key Usage .: "));
- delim = "";
-
- if (subkey->can_encrypt) {
- fprintf (fp, "%s%s", delim, _("encryption"));
- delim = _(", ");
- }
- if (subkey->can_sign) {
- fprintf (fp, "%s%s", delim, _("signing"));
- delim = _(", ");
- }
- if (subkey->can_certify) {
- fprintf (fp, "%s%s", delim, _("certification"));
- delim = _(", ");
- }
- putc ('\n', fp);
- }
- }
-
- if (Locale)
- setlocale (LC_TIME, "C");
-}
-
-
-/* Show detailed information about the selected key */
-static void verify_key (crypt_key_t * key)
-{
- FILE *fp;
- char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
- const char *s;
- gpgme_ctx_t listctx = NULL;
- gpgme_error_t err;
- gpgme_key_t k = NULL;
- int maxdepth = 100;
-
- fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
- if (!fp) {
- mutt_perror (_("Can't create temporary file"));
- return;
- }
- mutt_message _("Collecting data...");
-
- print_key_info (key->kobj, fp);
-
- err = gpgme_new (&listctx);
- if (err) {
- fprintf (fp, "Internal error: can't create gpgme context: %s\n",
- gpgme_strerror (err));
- goto leave;
- }
- if ((key->flags & KEYFLAG_ISX509))
- gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
-
- k = key->kobj;
- gpgme_key_ref (k);
- while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
- putc ('\n', fp);
- err = gpgme_op_keylist_start (listctx, s, 0);
- gpgme_key_release (k);
- k = NULL;
- if (!err)
- err = gpgme_op_keylist_next (listctx, &k);
- if (err) {
- fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
- goto leave;
- }
- gpgme_op_keylist_end (listctx);
-
- print_key_info (k, fp);
- if (!--maxdepth) {
- putc ('\n', fp);
- fputs (_("Error: certification chain to long - stopping here\n"), fp);
- break;
- }
- }
-
-leave:
- gpgme_key_release (k);
- gpgme_release (listctx);
- m_fclose(&fp);
- mutt_clear_error ();
- snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
- mutt_do_pager (cmd, tempfile, 0, NULL);
-}
-
-/* Implementation of `findkeys'. */
-
-/* Convert string_list_t into a pattern string suitable to be passed to GPGME.
- We need to convert spaces in an item into a '+' and '%' into
- "%25". */
-static char *list_to_pattern (string_list_t * list)
-{
- string_list_t *l;
- char *pattern, *p;
- const char *s;
- ssize_t n;
-
- n = 0;
- for (l = list; l; l = l->next) {
- for (s = l->data; *s; s++) {
- if (*s == '%')
- n += 2;
- n++;
- }
- n++; /* delimiter or end of string */
- }
- n++; /* make sure to allocate at least one byte */
- pattern = p = p_new(char, n);
- for (l = list; l; l = l->next) {
- s = l->data;
- if (*s) {
- if (l != list)
- *p++ = ' ';
- for (s = l->data; *s; s++) {
- if (*s == '%') {
- *p++ = '%';
- *p++ = '2';
- *p++ = '5';
- }
- else if (*s == '+') {
- *p++ = '%';
- *p++ = '2';
- *p++ = 'B';
- }
- else if (*s == ' ')
- *p++ = '+';
- else
- *p++ = *s;
- }
- }
- }
- *p = 0;
- return pattern;
-}
-
-/* Return a list of keys which are candidates for the selection.
- Select by looking at the HINTS list. */
-static crypt_key_t *get_candidates (string_list_t * hints, unsigned int app,
- int secret)
-{
- crypt_key_t *db, *k, **kend;
- char *pattern;
- gpgme_error_t err;
- gpgme_ctx_t ctx;
- gpgme_key_t key;
- int idx;
- gpgme_user_id_t uid = NULL;
-
- pattern = list_to_pattern (hints);
- if (!pattern)
- return NULL;
-
- err = gpgme_new (&ctx);
- if (err) {
- mutt_error (_("gpgme_new failed: %s"), gpgme_strerror (err));
- p_delete(&pattern);
- return NULL;
- }
-
- db = NULL;
- kend = &db;
-
- if ((app & APPLICATION_PGP)) {
- /* Its all a mess. That old GPGME expects different things
- depending on the protocol. For gpg we don' t need percent
- escaped pappert but simple strings passed in an array to the
- keylist_ext_start function. */
- string_list_t *l;
- ssize_t n;
- char **patarr;
-
- for (l = hints, n = 0; l; l = l->next) {
- if (l->data && *l->data)
- n++;
- }
- if (!n)
- goto no_pgphints;
-
- patarr = p_new(char *, n + 1);
- for (l = hints, n = 0; l; l = l->next) {
- if (l->data && *l->data)
- patarr[n++] = m_strdup(l->data);
- }
- patarr[n] = NULL;
- err = gpgme_op_keylist_ext_start (ctx, (const char **) patarr, secret, 0);
- for (n = 0; patarr[n]; n++)
- p_delete(&patarr[n]);
- p_delete(&patarr);
- if (err) {
- mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
- gpgme_release (ctx);
- p_delete(&pattern);
- return NULL;
- }
-
- while (!(err = gpgme_op_keylist_next (ctx, &key))) {
- unsigned int flags = 0;
-
- if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
- flags |= KEYFLAG_CANENCRYPT;
- if (key_check_cap (key, KEY_CAP_CAN_SIGN))
- flags |= KEYFLAG_CANSIGN;
-
- for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
- k = p_new(crypt_key_t, 1);
- k->kobj = key;
- k->idx = idx;
- k->uid = uid->uid;
- k->flags = flags;
- *kend = k;
- kend = &k->next;
- }
- }
- if (gpg_err_code (err) != GPG_ERR_EOF)
- mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
- gpgme_op_keylist_end (ctx);
- no_pgphints:
- ;
- }
-
- if ((app & APPLICATION_SMIME)) {
- /* and now look for x509 certificates */
- gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
- err = gpgme_op_keylist_start (ctx, pattern, 0);
- if (err) {
- mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
- gpgme_release (ctx);
- p_delete(&pattern);
- return NULL;
- }
-
- while (!(err = gpgme_op_keylist_next (ctx, &key))) {
- unsigned int flags = KEYFLAG_ISX509;
-
- if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
- flags |= KEYFLAG_CANENCRYPT;
- if (key_check_cap (key, KEY_CAP_CAN_SIGN))
- flags |= KEYFLAG_CANSIGN;
-
- for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
- k = p_new(crypt_key_t, 1);
- k->kobj = key;
- k->idx = idx;
- k->uid = uid->uid;
- k->flags = flags;
- *kend = k;
- kend = &k->next;
- }
- }
- if (gpg_err_code (err) != GPG_ERR_EOF)
- mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
- gpgme_op_keylist_end (ctx);
- }
-
- gpgme_release (ctx);
- p_delete(&pattern);
- return db;
-}
-
-/* Add the string STR to the list HINTS. This list is later used to
- match addresses. */
-static string_list_t *crypt_add_string_to_hints (string_list_t * hints, const char *str)
-{
- char *scratch;
- char *t;
-
- if ((scratch = m_strdup(str)) == NULL)
- return hints;
-
- for (t = strtok (scratch, " ,.:\"()<>\n"); t;
- t = strtok (NULL, " ,.:\"()<>\n")) {
- if (m_strlen(t) > 3)
- hints = mutt_add_list(hints, t);
- }
-
- p_delete(&scratch);
- return hints;
-}
-
-/* Display a menu to select a key from the array KEYS. FORCED_VALID
- will be set to true on return if the user did override the the
- key's validity. */
-static crypt_key_t *crypt_select_key (crypt_key_t * keys,
- address_t * p, const char *s,
- unsigned int app, int *forced_valid)
-{
- int keymax;
- crypt_key_t **key_table;
- MUTTMENU *menu;
- int i, done = 0;
- char helpstr[STRING], buf[LONG_STRING];
- crypt_key_t *k;
- int (*f) (const void *, const void *);
- int menu_to_use = 0;
- int unusable = 0;
-
- *forced_valid = 0;
-
- /* build the key table */
- keymax = i = 0;
- key_table = NULL;
- for (k = keys; k; k = k->next) {
- if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
- unusable = 1;
- continue;
- }
-
- if (i == keymax) {
- keymax += 20;
- p_realloc(&key_table, keymax);
- }
-
- key_table[i++] = k;
- }
-
- if (!i && unusable) {
- mutt_error _("All matching keys are marked expired/revoked.");
-
- mutt_sleep (1);
- return NULL;
- }
-
- switch (PgpSortKeys & SORT_MASK) {
- case SORT_DATE:
- f = crypt_compare_date;
- break;
- case SORT_KEYID:
- f = crypt_compare_keyid;
- break;
- case SORT_ADDRESS:
- f = crypt_compare_address;
- break;
- case SORT_TRUST:
- default:
- f = crypt_compare_trust;
- break;
- }
- qsort (key_table, i, sizeof (crypt_key_t *), f);
-
- if (app & APPLICATION_PGP)
- menu_to_use = MENU_KEY_SELECT_PGP;
- else if (app & APPLICATION_SMIME)
- menu_to_use = MENU_KEY_SELECT_SMIME;
-
- helpstr[0] = 0;
- mutt_make_help (buf, sizeof (buf), _("Exit "), menu_to_use, OP_EXIT);
- m_strcat(helpstr, sizeof(helpstr), buf);
- mutt_make_help (buf, sizeof (buf), _("Select "), menu_to_use,
- OP_GENERIC_SELECT_ENTRY);
- m_strcat(helpstr, sizeof(helpstr), buf);
- mutt_make_help (buf, sizeof (buf), _("Check key "),
- menu_to_use, OP_VERIFY_KEY);
- m_strcat(helpstr, sizeof(helpstr), buf);
- mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
- m_strcat(helpstr, sizeof(helpstr), buf);
-
- menu = mutt_new_menu ();
- menu->max = i;
- menu->make_entry = crypt_entry;
- menu->menu = menu_to_use;
- menu->help = helpstr;
- menu->data = key_table;
-
- {
- const char *ts;
-
- if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
- ts = _("PGP and S/MIME keys matching");
- else if ((app & APPLICATION_PGP))
- ts = _("PGP keys matching");
- else if ((app & APPLICATION_SMIME))
- ts = _("S/MIME keys matching");
- else
- ts = _("keys matching");
-
- if (p)
- snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
- else
- snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
- menu->title = buf;
- }
-
- mutt_clear_error ();
- k = NULL;
- while (!done) {
- *forced_valid = 0;
- switch (mutt_menuLoop (menu)) {
- case OP_VERIFY_KEY:
- verify_key (key_table[menu->current]);
- menu->redraw = REDRAW_FULL;
- break;
-
- case OP_VIEW_ID:
- mutt_message ("%s", key_table[menu->current]->uid);
- break;
-
- case OP_GENERIC_SELECT_ENTRY:
- /* FIXME make error reporting more verbose - this should be
- easy because gpgme provides more information */
- if (option (OPTPGPCHECKTRUST)) {
- if (!crypt_key_is_valid (key_table[menu->current])) {
- mutt_error _("This key can't be used: "
- "expired/disabled/revoked.");
- break;
- }
- }
-
- if (option (OPTPGPCHECKTRUST) &&
- (!crypt_id_is_valid (key_table[menu->current])
- || !crypt_id_is_strong (key_table[menu->current]))) {
- const char *warn_s;
- char buff[LONG_STRING];
-
- if (key_table[menu->current]->flags & KEYFLAG_CANTUSE)
- s = N_("ID is expired/disabled/revoked.");
- else {
- gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
- gpgme_user_id_t uid = NULL;
- int j = 0;
-
- warn_s = "??";
-
- uid = key_table[menu->current]->kobj->uids;
- for (j = 0; (j < key_table[menu->current]->idx) && uid;
- j++, uid = uid->next);
- if (uid)
- val = uid->validity;
-
- switch (val) {
- case GPGME_VALIDITY_UNKNOWN:
- case GPGME_VALIDITY_UNDEFINED:
- warn_s = N_("ID has undefined validity.");
- break;
- case GPGME_VALIDITY_NEVER:
- warn_s = N_("ID is not valid.");
- break;
- case GPGME_VALIDITY_MARGINAL:
- warn_s = N_("ID is only marginally valid.");
- break;
- case GPGME_VALIDITY_FULL:
- case GPGME_VALIDITY_ULTIMATE:
- break;
- }
-
- snprintf (buff, sizeof (buff),
- _("%s Do you really want to use the key?"), _(warn_s));
-
- if (mutt_yesorno (buff, 0) != 1) {
- mutt_clear_error ();
- break;
- }
- *forced_valid = 1;
- }
- }
-
- k = crypt_copy_key (key_table[menu->current]);
- done = 1;
- break;
-
- case OP_EXIT:
- k = NULL;
- done = 1;
- break;
- }
- }
-
- mutt_menuDestroy (&menu);
- p_delete(&key_table);
-
- set_option (OPTNEEDREDRAW);
-
- return k;
-}
-
-static crypt_key_t *crypt_getkeybyaddr (address_t * a, short abilities,
- unsigned int app, int *forced_valid)
-{
- address_t *r, *p;
- string_list_t *hints = NULL;
-
- int weak = 0;
- int invalid = 0;
- int multi = 0;
- int this_key_has_strong;
- int this_key_has_weak;
- int this_key_has_invalid;
- int match;
-
- crypt_key_t *keys, *k;
- crypt_key_t *the_valid_key = NULL;
- crypt_key_t *matches = NULL;
- crypt_key_t **matches_endp = &matches;
-
- *forced_valid = 0;
-
- if (a && a->mailbox)
- hints = crypt_add_string_to_hints (hints, a->mailbox);
- if (a && a->personal)
- hints = crypt_add_string_to_hints (hints, a->personal);
-
- mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
- keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
-
- string_list_wipe(&hints);
-
- if (!keys)
- return NULL;
-
- for (k = keys; k; k = k->next) {
- if (abilities && !(k->flags & abilities)) {
- continue;
- }
-
- this_key_has_weak = 0; /* weak but valid match */
- this_key_has_invalid = 0; /* invalid match */
- this_key_has_strong = 0; /* strong and valid match */
- match = 0; /* any match */
-
- r = rfc822_parse_adrlist (NULL, k->uid);
- for (p = r; p; p = p->next) {
- int validity = crypt_id_matches_addr (a, p, k);
-
- if (validity & CRYPT_KV_MATCH) /* something matches */
- match = 1;
-
- /* is this key a strong candidate? */
- if ((validity & CRYPT_KV_VALID)
- && (validity & CRYPT_KV_STRONGID)
- && (validity & CRYPT_KV_ADDR)) {
- if (the_valid_key && the_valid_key != k)
- multi = 1;
- the_valid_key = k;
- this_key_has_strong = 1;
- }
- else if ((validity & CRYPT_KV_MATCH)
- && !(validity & CRYPT_KV_VALID))
- this_key_has_invalid = 1;
- else if ((validity & CRYPT_KV_MATCH)
- && (!(validity & CRYPT_KV_STRONGID)
- || !(validity & CRYPT_KV_ADDR)))
- this_key_has_weak = 1;
- }
- address_list_wipe(&r);
-
- if (match) {
- crypt_key_t *tmp;
-
- if (!this_key_has_strong && this_key_has_invalid)
- invalid = 1;
- if (!this_key_has_strong && this_key_has_weak)
- weak = 1;
-
- *matches_endp = tmp = crypt_copy_key (k);
- matches_endp = &tmp->next;
- the_valid_key = tmp;
- }
- }
-
- crypt_free_key (&keys);
-
- if (matches) {
- if (the_valid_key && !multi && !weak
- && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
- /*
- * There was precisely one strong match on a valid ID, there
- * were no valid keys with weak matches, and we aren't
- * interested in seeing invalid keys.
- *
- * Proceed without asking the user.
- */
- k = crypt_copy_key (the_valid_key);
- }
- else {
- /*
- * Else: Ask the user.
- */
- k = crypt_select_key (matches, a, NULL, app, forced_valid);
- }
- crypt_free_key (&matches);
- }
- else
- k = NULL;
-
- return k;
-}
-
-
-static crypt_key_t *crypt_getkeybystr (const char *p, short abilities,
- unsigned int app, int *forced_valid)
-{
- string_list_t *hints = NULL;
- crypt_key_t *keys;
- crypt_key_t *matches = NULL;
- crypt_key_t **matches_endp = &matches;
- crypt_key_t *k;
- int match;
-
- mutt_message (_("Looking for keys matching \"%s\"..."), p);
-
- *forced_valid = 0;
-
- hints = crypt_add_string_to_hints (hints, p);
- keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
- string_list_wipe(&hints);
-
- if (!keys)
- return NULL;
-
- for (k = keys; k; k = k->next) {
- if (abilities && !(k->flags & abilities))
- continue;
-
- match = 0;
-
- if (!*p || !m_strcasecmp(p, crypt_keyid (k))
- || (!m_strncasecmp(p, "0x", 2)
- && !m_strcasecmp(p + 2, crypt_keyid (k)))
- || (option (OPTPGPLONGIDS)
- && !m_strncasecmp(p, "0x", 2)
- && !m_strcasecmp(p + 2, crypt_keyid (k) + 8))
- || m_stristr(k->uid, p)) {
- crypt_key_t *tmp;
-
- *matches_endp = tmp = crypt_copy_key (k);
- matches_endp = &tmp->next;
- }
- }
-
- crypt_free_key (&keys);
-
- if (matches) {
- k = crypt_select_key (matches, NULL, p, app, forced_valid);
- crypt_free_key (&matches);
- return k;
- }
-
- return NULL;
-}
-
-/* Display TAG as a prompt to ask for a key. If WHATFOR is not null
- use it as default and store it under that label as the next
- default. ABILITIES describe the required key abilities (sign,
- encrypt) and APP the type of the requested key; ether S/MIME or
- PGP. Return a copy of the key or NULL if not found. */
-static crypt_key_t *crypt_ask_for_key (char *tag,
- char *whatfor,
- short abilities,
- unsigned int app, int *forced_valid)
-{
- crypt_key_t *key;
- char resp[STRING];
- struct crypt_cache *l = NULL;
- int dummy;
-
- if (!forced_valid)
- forced_valid = &dummy;
-
- mutt_clear_error ();
-
- *forced_valid = 0;
- resp[0] = 0;
- if (whatfor) {
-
- for (l = id_defaults; l; l = l->next)
- if (!m_strcasecmp(whatfor, l->what)) {
- m_strcpy(resp, sizeof(resp), NONULL(l->dflt));
- break;
- }
- }
-
-
- for (;;) {
- resp[0] = 0;
- if (mutt_get_field (tag, resp, sizeof (resp), M_CLEAR) != 0)
- return NULL;
-
- if (whatfor) {
- if (l)
- m_strreplace(&l->dflt, resp);
- else {
- l = p_new(struct crypt_cache, 1);
- l->next = id_defaults;
- id_defaults = l;
- l->what = m_strdup(whatfor);
- l->dflt = m_strdup(resp);
- }
- }
-
- if ((key = crypt_getkeybystr (resp, abilities, app, forced_valid)))
- return key;
-
- BEEP ();
- }
- /* not reached */
-}
-
-/* This routine attempts to find the keyids of the recipients of a
- message. It returns NULL if any of the keys can not be found. */
-static char *find_keys (address_t * to, address_t * cc, address_t * bcc,
- unsigned int app)
-{
- char *keylist = NULL, *t;
- const char *keyID;
- ssize_t keylist_size = 0;
- ssize_t keylist_used = 0;
- address_t *tmp = NULL, *addr = NULL;
- address_t **last = &tmp;
- address_t *p, *q;
- int i;
- crypt_key_t *k_info, *key;
- const char *fqdn = mutt_fqdn (1);
-
-#if 0
- *r_application = APPLICATION_PGP | APPLICATION_SMIME;
-#endif
-
- for (i = 0; i < 3; i++) {
- switch (i) {
- case 0:
- p = to;
- break;
- case 1:
- p = cc;
- break;
- case 2:
- p = bcc;
- break;
- default:
- abort ();
- }
-
- *last = address_list_dup (p);
- while (*last)
- last = &((*last)->next);
- }
-
- rfc822_qualify(tmp, fqdn);
- address_list_uniq(tmp);
-
- for (p = tmp; p; p = p->next) {
- char buf[LONG_STRING];
- int forced_valid = 0;
-
- q = p;
- k_info = NULL;
-
- if ((keyID = mutt_crypt_hook (p)) != NULL) {
- int r;
-
- snprintf (buf, sizeof (buf), _("Use keyID = \"%s\" for %s?"),
- keyID, p->mailbox);
- if ((r = mutt_yesorno (buf, M_YES)) == M_YES) {
- /* check for e-mail address */
- if ((t = strchr (keyID, '@')) &&
- (addr = rfc822_parse_adrlist (NULL, keyID))) {
- rfc822_qualify(addr, fqdn);
- q = addr;
- }
- else {
- k_info = crypt_getkeybystr (keyID, KEYFLAG_CANENCRYPT,
- app, &forced_valid);
- }
- }
- else if (r == -1) {
- p_delete(&keylist);
- address_list_wipe(&tmp);
- address_list_wipe(&addr);
- return NULL;
- }
- }
-
- if (k_info == NULL
- && (k_info = crypt_getkeybyaddr (q, KEYFLAG_CANENCRYPT,
- app, &forced_valid)) == NULL) {
- snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
-
- if ((key = crypt_ask_for_key (buf, q->mailbox, KEYFLAG_CANENCRYPT,
- app,
- &forced_valid)) == NULL) {
- p_delete(&keylist);
- address_list_wipe(&tmp);
- address_list_wipe(&addr);
- return NULL;
- }
- }
- else
- key = k_info;
-
- {
- const char *s = crypt_fpr (key);
-
- keylist_size += m_strlen(s) + 4 + 1;
- p_realloc(&keylist, keylist_size);
- sprintf (keylist + keylist_used, "%s0x%s%s",
- keylist_used ? " " : "", s, forced_valid ? "!" : "");
- }
- keylist_used = m_strlen(keylist);
-
- crypt_free_key (&key);
- address_list_wipe(&addr);
- }
- address_list_wipe(&tmp);
- return (keylist);
-}
-
-int crypt_get_keys (HEADER * msg, char **keylist)
-{
- /* Do a quick check to make sure that we can find all of the encryption
- * keys if the user has requested this service.
- */
-
- *keylist = NULL;
-
- if (msg->security & ENCRYPT) {
- if (msg->security & APPLICATION_PGP) {
- set_option(OPTPGPCHECKTRUST);
- *keylist = find_keys(msg->env->to, msg->env->cc, msg->env->bcc,
- APPLICATION_PGP);
- unset_option(OPTPGPCHECKTRUST);
- if (!*keylist)
- return -1;
- }
-
- if (msg->security & APPLICATION_SMIME) {
- *keylist = find_keys(msg->env->to, msg->env->cc, msg->env->bcc,
- APPLICATION_SMIME);
- if (!*keylist)
- return -1;
- }
- }
-
- return (0);
-}
-
-
-int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
-{
- crypt_key_t *p;
- char input_signas[STRING];
- int choice;
-
- if (msg->security & APPLICATION_PGP)
- is_smime = 0;
- else if (msg->security & APPLICATION_SMIME)
- is_smime = 1;
-
- if (is_smime)
- choice =
- mutt_multi_choice (_
- ("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
- _("esabpc"));
- else
- choice =
- mutt_multi_choice (_
- ("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
- _("esabmc"));
-
- switch (choice) {
- case 1: /* (e)ncrypt */
- msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
- msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
- break;
-
- case 2: /* (s)ign */
- msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
- msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
- break;
-
- case 3: /* sign (a)s */
- if ((p = crypt_ask_for_key (_("Sign as: "), NULL, KEYFLAG_CANSIGN,
- is_smime ? APPLICATION_SMIME :
- APPLICATION_PGP, NULL))) {
- snprintf (input_signas, sizeof (input_signas), "0x%s", crypt_keyid (p));
- m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs,
- input_signas);
- crypt_free_key (&p);
-
- msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
- }
- *redraw = REDRAW_FULL;
- break;
-
- case 4: /* (b)oth */
- msg->security =
- (is_smime ? (SMIMEENCRYPT | SMIMESIGN) : (PGPENCRYPT | PGPSIGN));
- break;
-
- case 5: /* (p)gp or s/(m)ime */
- is_smime = !is_smime;
- break;
-
- case 6: /* (c)lear */
- return msg->security = 0;
- }
-
- if (is_smime) {
- msg->security &= ~APPLICATION_PGP;
- msg->security |= APPLICATION_SMIME;
- } else {
- msg->security &= ~APPLICATION_SMIME;
- msg->security |= APPLICATION_PGP;
- }
-
- return msg->security;
-}
-
-int crypt_smime_verify_sender (HEADER * h)
-{
- address_t *sender = NULL;
- unsigned int ret = 1;
-
- if (h->env->from) {
- h->env->from = mutt_expand_aliases (h->env->from);
- sender = h->env->from;
- }
- else if (h->env->sender) {
- h->env->sender = mutt_expand_aliases (h->env->sender);
- sender = h->env->sender;
- }
-
- if (sender) {
- if (signature_key) {
- gpgme_key_t key = signature_key;
- gpgme_user_id_t uid = NULL;
- int sender_length = 0;
- int uid_length = 0;
-
- sender_length = m_strlen(sender->mailbox);
- for (uid = key->uids; uid && ret; uid = uid->next) {
- uid_length = m_strlen(uid->email);
- if (1 && (uid->email[0] == '<')
- && (uid->email[uid_length - 1] == '>')
- && (uid_length == sender_length + 2)
- && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
- ret = 0;
- }
- }
- else
- mutt_any_key_to_continue ("Failed to verify sender");
- }
- else
- mutt_any_key_to_continue ("Failed to figure out sender");
-
- if (signature_key) {
- gpgme_key_release (signature_key);
- signature_key = NULL;
- }
-
- return ret;
-}
-
-void crypt_invoke_import(FILE *stream, int smime)
-{
- gpgme_ctx_t ctx = create_gpgme_context(smime);
- gpgme_data_t data;
- gpgme_error_t err;
-
- err = gpgme_data_new_from_stream(&data, stream);
- if (err) {
- mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
- gpgme_release(ctx);
- return;
- }
-
- err = gpgme_op_import(ctx, data);
- if (err) {
- mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
- gpgme_data_release(data);
- gpgme_release(ctx);
- return;
- }
-
- gpgme_data_release(data);
- gpgme_release(ctx);
- return;
-}
-
-static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
-{
- STATE s;
- FILE *tmpfp = tmpfile();
-
- if (tmpfp == NULL) {
- mutt_perror (_("Can't create temporary file"));
- return;
- }
-
- p_clear(&s, 1);
- s.fpin = fp;
- s.fpout = tmpfp;
- mutt_body_handler(top, &s);
-
- rewind(tmpfp);
- crypt_invoke_import(tmpfp, 0);
- m_fclose(&tmpfp);
-}
-
-void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
-{
- mutt_endwin (NULL);
- set_option (OPTDONTHANDLEPGPKEYS);
-
- for (; top; top = top->next) {
- if (!tag || top->tagged)
- pgp_extract_keys_from_attachment (fp, top);
-
- if (!tag)
- break;
- }
-
- unset_option (OPTDONTHANDLEPGPKEYS);
-}
-
-
-/* TODO */
-
-/* fixme: needs documentation. */
-void crypt_pgp_invoke_getkeys (address_t * addr)
-{
-}
-
-/* Generate a PGP public key attachment. */
-BODY *crypt_pgp_make_key_attachment (char *tempf)
-{
- return NULL;
-}
-
-/* S/MIME */
-
-/* fixme: Needs documentation. */
-void crypt_smime_getkeys (ENVELOPE * env)
-{
-}
-