X-Git-Url: https://git.llucax.com/software/mutt-debian.git/blobdiff_plain/14c29200cb58d3c4a0830265f2433849781858d0..228ef588576668a0ae8d0d2d89ad9ab672b35282:/crypt-gpgme.c diff --git a/crypt-gpgme.c b/crypt-gpgme.c index 3809daa..2dc4f0a 100644 --- a/crypt-gpgme.c +++ b/crypt-gpgme.c @@ -1,5 +1,5 @@ /* crypt-gpgme.c - GPGME based crypto operations - * Copyright (C) 1996,1997 Michael R. Elkins + * Copyright (C) 1996-7,2007 Michael R. Elkins * Copyright (C) 1998,1999,2000 Thomas Roessler * Copyright (C) 2001 Thomas Roessler * Oliver Ehli @@ -82,6 +82,9 @@ #define CRYPT_KV_STRONGID 8 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING) +/* static local variables */ +static int GpgmeLocaleSet = 0; + /* * Type definitions. */ @@ -339,6 +342,16 @@ static gpgme_ctx_t create_gpgme_context (int for_smime) 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) { @@ -427,12 +440,12 @@ static gpgme_data_t body_to_data_object (BODY *a, int convert) 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); @@ -531,7 +544,7 @@ static char *data_object_to_tempfile (gpgme_data_t data, FILE **ret_fp) if (fwrite (buf, nread, 1, fp) != 1) { mutt_perror (tempfile); - fclose (fp); + safe_fclose (&fp); unlink (tempfile); return NULL; } @@ -540,12 +553,12 @@ static char *data_object_to_tempfile (gpgme_data_t data, FILE **ret_fp) 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) @@ -756,7 +769,7 @@ static char *encrypt_gpgme_object (gpgme_data_t plaintext, gpgme_key_t *rset, 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; @@ -771,9 +784,16 @@ static int get_micalg (gpgme_ctx_t ctx, char *buf, size_t buflen) 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); + } } } @@ -873,10 +893,10 @@ static BODY *sign_message (BODY *a, int use_smime) &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; @@ -1245,6 +1265,40 @@ static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE *s) 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 @@ -1254,12 +1308,10 @@ static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE *s) 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; @@ -1283,7 +1335,6 @@ static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE *s) signature_key = NULL; } - created = sig->timestamp; fpr = sig->fpr; sum = sig->summary; @@ -1308,63 +1359,43 @@ static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE *s) ; /* 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); @@ -1757,7 +1788,7 @@ int smime_gpgme_decrypt_mime (FILE *fpin, FILE **fpout, BODY *b, BODY **cur) 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)) { @@ -1793,7 +1824,7 @@ int smime_gpgme_decrypt_mime (FILE *fpin, FILE **fpout, BODY *b, BODY **cur) bb->length = ftello (s.fpout); bb->offset = 0; rewind (tmpfp); - fclose (*fpout); + safe_fclose (fpout); memset (&s, 0, sizeof (s)); s.fpin = tmpfp; @@ -1812,7 +1843,7 @@ int smime_gpgme_decrypt_mime (FILE *fpin, FILE **fpout, BODY *b, BODY **cur) 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; @@ -1820,6 +1851,120 @@ int smime_gpgme_decrypt_mime (FILE *fpin, FILE **fpout, BODY *b, BODY **cur) 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'. @@ -1903,6 +2048,37 @@ int pgp_gpgme_check_traditional (FILE *fp, BODY *b, int tagged_only) 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'. @@ -1971,7 +2147,7 @@ static void copy_clearsigned (gpgme_data_t data, STATE *s, char *charset) } fgetconv_close (&fc); - fclose (fp); + safe_fclose (&fp); } @@ -2026,12 +2202,11 @@ int pgp_gpgme_application_handler (BODY *m, STATE *s) 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 */ @@ -2047,7 +2222,11 @@ int pgp_gpgme_application_handler (BODY *m, STATE *s) /* 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; @@ -2089,7 +2268,7 @@ int pgp_gpgme_application_handler (BODY *m, STATE *s) 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); @@ -2192,13 +2371,16 @@ int pgp_gpgme_application_handler (BODY *m, STATE *s) 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); @@ -2207,7 +2389,7 @@ int pgp_gpgme_application_handler (BODY *m, STATE *s) { 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")); @@ -2291,7 +2473,7 @@ int pgp_gpgme_encrypted_handler (BODY *a, STATE *s) mutt_free_body (&tattach); } - fclose (fpout); + safe_fclose (&fpout); mutt_unlink(tempfile); dprint (2, (debugfile, "Leaving pgp_encrypted handler\n")); @@ -2365,7 +2547,7 @@ int smime_gpgme_application_handler (BODY *a, STATE *s) mutt_free_body (&tattach); } - fclose (fpout); + safe_fclose (&fpout); mutt_unlink(tempfile); dprint (2, (debugfile, "Leaving smime_encrypted handler\n")); @@ -3321,7 +3503,7 @@ verify_key (crypt_key_t *key) 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); @@ -3647,10 +3829,9 @@ static crypt_key_t *crypt_select_key (crypt_key_t *keys, 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; @@ -4056,7 +4237,7 @@ static char *find_keys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc, default: abort (); } - *last = rfc822_cpy_adr (p); + *last = rfc822_cpy_adr (p, 0); while (*last) last = &((*last)->next); }