/* crypt-gpgme.c - GPGME based crypto operations
- * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
+ * Copyright (C) 1996-7,2007 Michael R. Elkins <me@cs.hmc.edu>
* Copyright (C) 1998,1999,2000 Thomas Roessler <roessler@does-not-exist.org>
* Copyright (C) 2001 Thomas Roessler <roessler@does-not-exist.org>
* Oliver Ehli <elmy@acm.org>
#define CRYPT_KV_STRONGID 8
#define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
+/* static local variables */
+static int GpgmeLocaleSet = 0;
+
/*
* Type definitions.
*/
gpgme_error_t err;
gpgme_ctx_t ctx;
+ if (!GpgmeLocaleSet)
+ {
+ gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
+#ifdef ENABLE_NLS
+ gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
+#endif
+
+ GpgmeLocaleSet = 1;
+ }
+
err = gpgme_new (&ctx);
if (err)
{
buf[0] = c;
gpgme_data_write (data, buf, 1);
}
- fclose(fptmp);
+ safe_fclose (&fptmp);
gpgme_data_seek (data, 0, SEEK_SET);
}
else
{
- fclose(fptmp);
+ safe_fclose (&fptmp);
err = gpgme_data_new_from_file (&data, tempfile, 1);
}
unlink (tempfile);
if (fwrite (buf, nread, 1, fp) != 1)
{
mutt_perror (tempfile);
- fclose (fp);
+ safe_fclose (&fp);
unlink (tempfile);
return NULL;
}
if (ret_fp)
rewind (fp);
else
- fclose (fp);
+ safe_fclose (&fp);
if (nread == -1)
{
mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
unlink (tempfile);
- fclose (fp);
+ safe_fclose (&fp);
return NULL;
}
if (ret_fp)
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, size_t buflen)
+static int get_micalg (gpgme_ctx_t ctx, int use_smime, char *buf, size_t buflen)
{
gpgme_sign_result_t result = NULL;
const char *algorithm_name = NULL;
algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
if (algorithm_name)
{
- /* convert GPGME raw hash name to RFC 3156 format */
- snprintf (buf, buflen, "pgp-%s", algorithm_name);
- ascii_strlower (buf + 4);
+ if (use_smime)
+ {
+ /* convert GPGME raw hash name to RFC 2633 format */
+ snprintf (buf, buflen, "%s", algorithm_name);
+ ascii_strlower (buf);
+ } else {
+ /* convert GPGME raw hash name to RFC 3156 format */
+ snprintf (buf, buflen, "pgp-%s", algorithm_name);
+ ascii_strlower (buf + 4);
+ }
}
}
&t->parameter);
/* 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))
+ if (!get_micalg (ctx, use_smime, buf, sizeof buf))
mutt_set_parameter ("micalg", buf, &t->parameter);
else if (use_smime)
- mutt_set_parameter ("micalg", "pgp-sha1", &t->parameter);
+ mutt_set_parameter ("micalg", "sha1", &t->parameter);
gpgme_release (ctx);
t->parts = a;
state_attach_puts (txt, s);
}
+static void print_smime_keyinfo (const char* msg, gpgme_signature_t sig,
+ gpgme_key_t key, STATE *s)
+{
+ size_t msglen;
+ gpgme_user_id_t uids = NULL;
+ int i, aka = 0;
+
+ state_attach_puts (msg, s);
+ state_attach_puts (" ", s);
+ for (uids = key->uids; uids; uids = uids->next)
+ {
+ if (uids->revoked)
+ continue;
+ if (aka)
+ {
+ msglen = mutt_strlen (msg) - 4;
+ for (i = 0; i < msglen; i++)
+ state_attach_puts(" ", s);
+ state_attach_puts(_("aka: "), s);
+ }
+ state_attach_puts (uids->uid, s);
+ state_attach_puts ("\n", s);
+
+ aka = 1;
+ }
+
+ msglen = mutt_strlen (msg) - 8;
+ for (i = 0; i < msglen; i++)
+ state_attach_puts(" ", s);
+ state_attach_puts (_("created: "), s);
+ print_time (sig->timestamp, s);
+ state_attach_puts ("\n", 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
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;
signature_key = NULL;
}
- created = sig->timestamp;
fpr = sig->fpr;
sum = sig->summary;
; /* 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;
+ 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, sig))
- anywarn = 1;
- show_one_sig_validity (ctx, idx, s);
- }
+ {
+ print_smime_keyinfo (_("Good signature from:"), sig, key, s);
+ if (show_sig_summary (sum, ctx, key, idx, s, sig))
+ 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, sig);
- }
+ {
+ print_smime_keyinfo (_("*BAD* signature from:"), sig, key, s);
+ show_sig_summary (sum, ctx, key, idx, s, sig);
+ }
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, sig))
- anywarn = 1;
- }
+ { /* 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). */
+ print_smime_keyinfo (_("Good signature from:"), sig, key, s);
+ show_one_sig_validity (ctx, idx, s);
+ show_fingerprint (key,s);
+ if (show_sig_summary (sum, ctx, key, idx, s, sig))
+ 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, sig);
- }
+ {
+ print_smime_keyinfo (_("Problem signature from:"), sig, key, s);
+ state_attach_puts (_(" expires: "), s);
+ print_time (sig->exp_timestamp, s);
+ state_attach_puts ("\n", s);
+ show_sig_summary (sum, ctx, key, idx, s, sig);
+ anywarn = 1;
+ }
if (key != signature_key)
gpgme_key_release (key);
b->type = saved_b_type;
b->length = saved_b_length;
b->offset = saved_b_offset;
- fclose (tmpfp);
+ safe_fclose (&tmpfp);
rewind (*fpout);
if (*cur && !is_signed && !(*cur)->parts && mutt_is_application_smime (*cur))
{
bb->length = ftello (s.fpout);
bb->offset = 0;
rewind (tmpfp);
- fclose (*fpout);
+ safe_fclose (fpout);
memset (&s, 0, sizeof (s));
s.fpin = tmpfp;
bb->type = saved_b_type;
bb->length = saved_b_length;
bb->offset = saved_b_offset;
- fclose (tmpfp);
+ safe_fclose (&tmpfp);
rewind (*fpout);
mutt_free_body (cur);
*cur = tmp_b;
return *cur? 0:-1;
}
+static int pgp_gpgme_extract_keys (gpgme_data_t keydata, FILE** fp, int dryrun)
+{
+ /* there's no side-effect free way to view key data in GPGME,
+ * so we import the key into a temporary keyring */
+ char tmpdir[_POSIX_PATH_MAX];
+ char tmpfile[_POSIX_PATH_MAX];
+ gpgme_ctx_t tmpctx;
+ gpgme_error_t err;
+ gpgme_engine_info_t engineinfo;
+ gpgme_key_t key;
+ gpgme_user_id_t uid;
+ gpgme_subkey_t subkey;
+ const char* shortid;
+ int len;
+ char date[STRING];
+ int more;
+ int rc = -1;
+
+ if ((err = gpgme_new (&tmpctx)) != GPG_ERR_NO_ERROR)
+ {
+ dprint (1, (debugfile, "Error creating GPGME context\n"));
+ return rc;
+ }
+
+ if (dryrun)
+ {
+ snprintf (tmpdir, sizeof(tmpdir), "%s/mutt-gpgme-XXXXXX", Tempdir);
+ if (!mkdtemp (tmpdir))
+ {
+ dprint (1, (debugfile, "Error creating temporary GPGME home\n"));
+ goto err_ctx;
+ }
+
+ engineinfo = gpgme_ctx_get_engine_info (tmpctx);
+ while (engineinfo && engineinfo->protocol != GPGME_PROTOCOL_OpenPGP)
+ engineinfo = engineinfo->next;
+ if (!engineinfo)
+ {
+ dprint (1, (debugfile, "Error finding GPGME PGP engine\n"));
+ goto err_tmpdir;
+ }
+
+ err = gpgme_ctx_set_engine_info (tmpctx, GPGME_PROTOCOL_OpenPGP,
+ engineinfo->file_name, tmpdir);
+ if (err != GPG_ERR_NO_ERROR)
+ {
+ dprint (1, (debugfile, "Error setting GPGME context home\n"));
+ goto err_tmpdir;
+ }
+ }
+
+ if ((err = gpgme_op_import (tmpctx, keydata)) != GPG_ERR_NO_ERROR)
+ {
+ dprint (1, (debugfile, "Error importing key\n"));
+ goto err_tmpdir;
+ }
+
+ mutt_mktemp (tmpfile);
+ *fp = safe_fopen (tmpfile, "w+");
+ if (!*fp)
+ {
+ mutt_perror (tmpfile);
+ goto err_tmpdir;
+ }
+ unlink (tmpfile);
+
+ err = gpgme_op_keylist_start (tmpctx, NULL, 0);
+ while (!err)
+ {
+ if ((err = gpgme_op_keylist_next (tmpctx, &key)))
+ break;
+ uid = key->uids;
+ subkey = key->subkeys;
+ more = 0;
+ while (subkey)
+ {
+ shortid = subkey->keyid;
+ len = mutt_strlen (subkey->keyid);
+ if (len > 8)
+ shortid += len - 8;
+ strftime (date, sizeof (date), "%Y-%m-%d", localtime (&subkey->timestamp));
+
+ if (!more)
+ fprintf (*fp, "%s %5.5s %d/%8s %s %s\n", more ? "sub" : "pub",
+ gpgme_pubkey_algo_name (subkey->pubkey_algo), subkey->length,
+ shortid, date, uid->uid);
+ else
+ fprintf (*fp, "%s %5.5s %d/%8s %s\n", more ? "sub" : "pub",
+ gpgme_pubkey_algo_name (subkey->pubkey_algo), subkey->length,
+ shortid, date);
+ subkey = subkey->next;
+ more = 1;
+ }
+ gpgme_key_release (key);
+ }
+ if (gpg_err_code (err) != GPG_ERR_EOF)
+ {
+ dprint (1, (debugfile, "Error listing keys\n"));
+ goto err_fp;
+ }
+
+ rc = 0;
+
+err_fp:
+ if (rc)
+ safe_fclose (fp);
+err_tmpdir:
+ if (dryrun)
+ mutt_rmtree (tmpdir);
+err_ctx:
+ gpgme_release (tmpctx);
+
+ return rc;
+}
/*
* Implementation of `pgp_check_traditional'.
return rv;
}
+/* TODO: looks like this won't work and we'll have to fully parse the
+ * message file. GPGME makes life hard yet again. */
+void pgp_gpgme_invoke_import (const char *fname)
+{
+ gpgme_data_t keydata;
+ gpgme_error_t err;
+ FILE* in;
+ FILE* out;
+ long outlen;
+
+ if (!(in = safe_fopen (fname, "r")))
+ return;
+ if ((err = gpgme_data_new_from_stream (&keydata, in)) != GPG_ERR_NO_ERROR)
+ {
+ dprint (1, (debugfile, "error converting key file into data object\n"));
+ return;
+ }
+ safe_fclose (&in);
+
+ if (!pgp_gpgme_extract_keys (keydata, &out, 0))
+ {
+ /* display import results */
+ outlen = ftell (out);
+ fseek (out, 0, SEEK_SET);
+ mutt_copy_bytes (out, stdout, outlen);
+ safe_fclose (&out);
+ }
+ else
+ printf (_("Error extracting key data!\n"));
+}
+
/*
* Implementation of `application_handler'.
}
fgetconv_close (&fc);
- fclose (fp);
+ safe_fclose (&fp);
}
clearsign = 1;
needpass = 0;
}
- else if (!option (OPTDONTHANDLEPGPKEYS) &&
- !mutt_strcmp ("PUBLIC KEY BLOCK-----\n", buf + 15))
- {
- needpass = 0;
- pgp_keyblock =1;
- }
+ else if (!mutt_strcmp ("PUBLIC KEY BLOCK-----\n", buf + 15))
+ {
+ needpass = 0;
+ pgp_keyblock = 1;
+ }
else
{
/* XXX - we may wish to recode here */
/* Copy PGP material to an data container */
armored_data = file_to_data_object (s->fpin, m->offset, m->length);
/* Invoke PGP if needed */
- if (!clearsign || (s->flags & M_VERIFY))
+ if (pgp_keyblock)
+ {
+ pgp_gpgme_extract_keys (armored_data, &pgpout, 1);
+ }
+ else if (!clearsign || (s->flags & M_VERIFY))
{
unsigned int sig_stat = 0;
gpgme_data_t plaintext;
char *tmpfname;
{
- /* Check wether signatures have been verified. */
+ /* Check whether signatures have been verified. */
gpgme_verify_result_t verify_result;
verify_result = gpgme_op_verify_result (ctx);
safe_fclose (&pgpout);
}
}
+#if 0
else
- {
- /* XXX - we may wish to recode here */
- if (s->prefix)
- state_puts (s->prefix, s);
- state_puts (buf, s);
- }
+ {
+ /* why would we want to display this at all? */
+ /* XXX - we may wish to recode here */
+ if (s->prefix)
+ state_puts (s->prefix, s);
+ state_puts (buf, s);
+ }
+#endif
}
m->goodsig = (maybe_goodsig && have_any_sigs);
{
state_attach_puts (_("[-- Error: could not find beginning"
" of PGP message! --]\n\n"), s);
- return -1;
+ return 1;
}
dprint (2, (debugfile, "Leaving pgp_application_pgp handler\n"));
mutt_free_body (&tattach);
}
- fclose (fpout);
+ safe_fclose (&fpout);
mutt_unlink(tempfile);
dprint (2, (debugfile, "Leaving pgp_encrypted handler\n"));
mutt_free_body (&tattach);
}
- fclose (fpout);
+ safe_fclose (&fpout);
mutt_unlink(tempfile);
dprint (2, (debugfile, "Leaving smime_encrypted handler\n"));
leave:
gpgme_key_release (k);
gpgme_release (listctx);
- fclose (fp);
+ safe_fclose (&fp);
mutt_clear_error ();
snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
mutt_do_pager (cmd, tempfile, 0, NULL);
mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
strcat (helpstr, buf); /* __STRCAT_CHECKED__ */
- menu = mutt_new_menu ();
+ menu = mutt_new_menu (menu_to_use);
menu->max = i;
menu->make_entry = crypt_entry;
- menu->menu = menu_to_use;
menu->help = helpstr;
menu->data = key_table;
default: abort ();
}
- *last = rfc822_cpy_adr (p);
+ *last = rfc822_cpy_adr (p, 0);
while (*last)
last = &((*last)->next);
}