From 54591341a417ca769b2219a9b2f7683f11a74718 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 13 Oct 2010 15:57:08 +0000 Subject: [PATCH] More agent support for gpg. --- NEWS | 5 +- agent/ChangeLog | 21 ++++ agent/agent.h | 4 +- agent/call-pinentry.c | 29 +++--- agent/command-ssh.c | 4 +- agent/command.c | 42 ++++---- agent/cvt-openpgp.c | 2 +- agent/divert-scd.c | 6 +- agent/findkey.c | 8 +- agent/genkey.c | 14 ++- agent/gpg-agent.c | 2 + agent/pkdecrypt.c | 2 - agent/pksign.c | 2 - agent/protect-tool.c | 3 +- common/ChangeLog | 5 + common/miscellaneous.c | 78 +++++++++++++++ common/util.h | 2 + doc/gpg.texi | 18 +++- g10/ChangeLog | 30 ++++++ g10/call-agent.c | 6 ++ g10/card-util.c | 1 + g10/getkey.c | 156 +++++++++++++++++------------- g10/gpg.c | 10 ++ g10/gpgv.c | 18 ++++ g10/import.c | 16 +++- g10/keydb.h | 5 +- g10/keyedit.c | 2 +- g10/keygen.c | 45 +++------ g10/keylist.c | 213 ++++++++++++++++++++++------------------- g10/misc.c | 7 +- g10/options.h | 2 + g10/passphrase.c | 14 +-- g10/pubkey-enc.c | 35 ++----- 33 files changed, 496 insertions(+), 311 deletions(-) diff --git a/NEWS b/NEWS index f895d3a65..290bbcd2f 100644 --- a/NEWS +++ b/NEWS @@ -43,10 +43,13 @@ Noteworthy changes in version 2.1.x (under development) * The OpenPGP import command is now able to merge secret keys. - * Removed options: + * Removed GPG options: --export-options: export-secret-subkey-passwd --simple-sk-checksum + * New GPG options: + --try-secret-key + Noteworthy changes in version 2.0.13 (2009-09-04) ------------------------------------------------- diff --git a/agent/ChangeLog b/agent/ChangeLog index 01ff96172..12a853281 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,24 @@ +2010-10-13 Werner Koch + + * call-pinentry.c (agent_get_passphrase): Support the close_button. + + * gpg-agent.c (create_server_socket): Switch back to stderr + logging if we are not starting a agent. + + * command.c (cmd_passwd, cmd_export_key): Move mapping of + GPG_ERR_FULLY_CANCELED to .. + (leave_cmd): .. here. + (option_handler): Add option agent-awareness. + * protect-tool.c (get_passphrase): Take care of + GPG_ERR_FULLY_CANCELED. + * findkey.c (try_unprotect_cb): Ditto. + (unprotect): Remove the fully_canceled hack. + * call-pinentry.c (start_pinentry): Ditto. + (agent_askpin): Ditto. + * pkdecrypt.c (agent_pkdecrypt): Ditto + * pksign.c (agent_pksign_do): Ditto. + * genkey.c (agent_ask_new_passphrase): Remove arg CANCEL_ALL. + 2010-10-06 Werner Koch * cvt-openpgp.c (convert_secret_key): Add missing break. diff --git a/agent/agent.h b/agent/agent.h index 517df1351..7276e66c3 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -253,7 +253,7 @@ int pinentry_active_p (ctrl_t ctrl, int waitseconds); int agent_askpin (ctrl_t ctrl, const char *desc_text, const char *prompt_text, const char *inital_errtext, - struct pin_entry_info_s *pininfo, int *r_cancelall); + struct pin_entry_info_s *pininfo); int agent_get_passphrase (ctrl_t ctrl, char **retpass, const char *desc, const char *prompt, const char *errtext, int with_qualitybar); @@ -291,7 +291,7 @@ int agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, /*-- genkey.c --*/ int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent); gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, - char **r_passphrase, int *r_cancelall); + char **r_passphrase); int agent_genkey (ctrl_t ctrl, const char *cache_nonce, const char *keyparam, size_t keyparmlen, membuf_t *outbuf); int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey); diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c index 4436b4f48..bbb15001f 100644 --- a/agent/call-pinentry.c +++ b/agent/call-pinentry.c @@ -467,8 +467,10 @@ start_pinentry (ctrl_t ctrl) else { rc = agent_inq_pinentry_launched (ctrl, pinentry_pid); - if (gpg_err_code (rc) == GPG_ERR_CANCELED) - return unlock_pinentry (gpg_error (GPG_ERR_CANCELED)); + if (gpg_err_code (rc) == GPG_ERR_CANCELED + || gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED) + return unlock_pinentry (gpg_err_make (GPG_ERR_SOURCE_DEFAULT, + gpg_err_code (rc))); rc = 0; } @@ -727,7 +729,7 @@ int agent_askpin (ctrl_t ctrl, const char *desc_text, const char *prompt_text, const char *initial_errtext, - struct pin_entry_info_s *pininfo, int *r_cancel_all) + struct pin_entry_info_s *pininfo) { int rc; char line[ASSUAN_LINELENGTH]; @@ -737,9 +739,6 @@ agent_askpin (ctrl_t ctrl, int saveflag; int close_button; - if (r_cancel_all) - *r_cancel_all = 0; - if (opt.batch) return 0; /* fixme: we should return BAD PIN */ @@ -830,10 +829,10 @@ agent_askpin (ctrl_t ctrl, && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); - /* Set a flag in case the window close button was clicked to - cancel the operation. */ - if (close_button && r_cancel_all && gpg_err_code (rc) == GPG_ERR_CANCELED) - *r_cancel_all = 1; + /* Change error code in case the window close button was clicked + to cancel the operation. */ + if (close_button && gpg_err_code (rc) == GPG_ERR_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED); if (gpg_err_code (rc) == GPG_ERR_ASS_TOO_MUCH_DATA) errtext = is_pin? _("PIN too long") @@ -890,6 +889,7 @@ agent_get_passphrase (ctrl_t ctrl, char line[ASSUAN_LINELENGTH]; struct entry_parm_s parm; int saveflag; + int close_button; *retpass = NULL; if (opt.batch) @@ -942,14 +942,21 @@ agent_get_passphrase (ctrl_t ctrl, saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL); assuan_begin_confidential (entry_ctx); + close_button = 0; rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, - inq_quality, entry_ctx, NULL, NULL); + inq_quality, entry_ctx, + close_button_status_cb, &close_button); assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag); /* Most pinentries out in the wild return the old Assuan error code for canceled which gets translated to an assuan Cancel error and not to the code for a user cancel. Fix this here. */ if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); + /* Change error code in case the window close button was clicked + to cancel the operation. */ + if (close_button && gpg_err_code (rc) == GPG_ERR_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED); + if (rc) xfree (parm.buffer); else diff --git a/agent/command-ssh.c b/agent/command-ssh.c index 3829d4836..ec1c73e6a 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -2425,7 +2425,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl) pi2->check_cb_arg = pi->pin; next_try: - err = agent_askpin (ctrl, description, NULL, initial_errtext, pi, NULL); + err = agent_askpin (ctrl, description, NULL, initial_errtext, pi); initial_errtext = NULL; if (err) goto out; @@ -2433,7 +2433,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl) /* Unless the passphrase is empty, ask to confirm it. */ if (pi->pin && *pi->pin) { - err = agent_askpin (ctrl, description2, NULL, NULL, pi2, NULL); + err = agent_askpin (ctrl, description2, NULL, NULL, pi2); if (err == -1) { /* The re-entered one did not match and the user did not hit cancel. */ diff --git a/agent/command.c b/agent/command.c index e8af3ecf6..5444e1811 100644 --- a/agent/command.c +++ b/agent/command.c @@ -71,6 +71,7 @@ struct server_local_s be done. */ void *import_key; /* Malloced KEK for the import_key command. */ void *export_key; /* Malloced KEK for the export_key command. */ + int allow_fully_canceled; /* Client is aware of GPG_ERR_FULLY_CANCELED. */ }; @@ -364,6 +365,16 @@ leave_cmd (assuan_context_t ctx, gpg_error_t err) if (!name) name = "?"; + /* Not all users of gpg-agent know about the fully canceled + error code; map it back if needed. */ + if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) + { + ctrl_t ctrl = assuan_get_pointer (ctx); + + if (!ctrl->server_local->allow_fully_canceled) + err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED); + } + /* Most code from common/ does not know the error source, thus we fix this here. */ if (gpg_err_source (err) == GPG_ERR_SOURCE_UNKNOWN) @@ -1336,12 +1347,7 @@ cmd_passwd (assuan_context_t ctx, char *line) grip, &shadow_info, CACHE_MODE_IGNORE, NULL, &s_skey, NULL); if (rc) - { - /* Not all users of gpg-agent know about fully cancled; thus we - map it back. */ - if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED) - rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); - } + ; else if (!s_skey) { log_error ("changing a smartcard PIN is not yet supported\n"); @@ -1643,7 +1649,7 @@ cmd_import_key (assuan_context_t ctx, char *line) err = agent_ask_new_passphrase (ctrl, _("Please enter the passphrase to protect the " "imported object within the GnuPG system."), - &passphrase, NULL); + &passphrase); if (err) goto leave; } @@ -1751,17 +1757,12 @@ cmd_export_key (assuan_context_t ctx, char *line) canonical S-expression. */ if (!passphrase) { - int fully_canceled; err = agent_ask_new_passphrase (ctrl, _("This key (or subkey) is not protected with a passphrase." " Please enter a new passphrase to export it."), - &passphrase, &fully_canceled); + &passphrase); if (err) - { - if (fully_canceled) - err = gpg_error (GPG_ERR_FULLY_CANCELED); - goto leave; - } + goto leave; } err = convert_to_openpgp (ctrl, s_skey, passphrase, &key, &keylen); } @@ -1814,10 +1815,6 @@ cmd_export_key (assuan_context_t ctx, char *line) xfree (ctrl->server_local->keydesc); ctrl->server_local->keydesc = NULL; - /* Not all users of gpg-agent know about fully cancled; thus we map - it back unless we know that it is okay. */ - if (!openpgp && gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) - err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED); return leave_cmd (ctx, err); } @@ -2185,7 +2182,14 @@ option_handler (assuan_context_t ctx, const char *key, const char *value) ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err = 0; - if (!strcmp (key, "putenv")) + if (!strcmp (key, "agent-awareness")) + { + /* The value is a version string telling us of which agent + version the caller is aware of. */ + ctrl->server_local->allow_fully_canceled = + gnupg_compare_version (value, "2.1.0"); + } + else if (!strcmp (key, "putenv")) { /* Change the session's environment to be used for the Pinentry. Valid values are: diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 5a044c449..6cedc88ea 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -780,7 +780,7 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, err = try_do_unprotect_cb (pi); } if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE) - err = agent_askpin (ctrl, prompt, NULL, NULL, pi, NULL); + err = agent_askpin (ctrl, prompt, NULL, NULL, pi); skeyidx = pi_arg.skeyidx; if (!err) { diff --git a/agent/divert-scd.c b/agent/divert-scd.c index 0986f3bd3..bf07d0785 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -266,7 +266,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) if (any_flags) { - rc = agent_askpin (ctrl, info, prompt, again_text, pi, NULL); + rc = agent_askpin (ctrl, info, prompt, again_text, pi); again_text = NULL; if (!rc && newpin) { @@ -288,7 +288,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) is_puk? _("Repeat this PUK"): _("Repeat this PIN")), - prompt, NULL, pi2, NULL); + prompt, NULL, pi2); if (!rc && strcmp (pi->pin, pi2->pin)) { again_text = (resetcode? @@ -312,7 +312,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) info? info:"", info? "')":"") < 0) desc = NULL; - rc = agent_askpin (ctrl, desc?desc:info, prompt, NULL, pi, NULL); + rc = agent_askpin (ctrl, desc?desc:info, prompt, NULL, pi); xfree (desc); } diff --git a/agent/findkey.c b/agent/findkey.c index 63b24a5d5..611a44142 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -169,7 +169,8 @@ try_unprotect_cb (struct pin_entry_info_s *pi) _("I'll change it later"), 0); if (!err) arg->change_required = 1; - else if (gpg_err_code (err) == GPG_ERR_CANCELED) + else if (gpg_err_code (err) == GPG_ERR_CANCELED + || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) err = 0; } xfree (desc); @@ -290,7 +291,6 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, unsigned char *result; size_t resultlen; char hexgrip[40+1]; - int fully_canceled; if (r_passphrase) *r_passphrase = NULL; @@ -383,9 +383,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, arg.change_required = 0; pi->check_cb_arg = &arg; - rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi, &fully_canceled); - if (gpg_err_code (rc) == GPG_ERR_CANCELED && fully_canceled) - rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_FULLY_CANCELED); + rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi); if (!rc) { assert (arg.unprotected_key); diff --git a/agent/genkey.c b/agent/genkey.c index c26785204..0a35643e5 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -290,12 +290,10 @@ reenter_compare_cb (struct pin_entry_info_s *pi) function returns 0 and store the passphrase at R_PASSPHRASE; if the user opted not to use a passphrase NULL will be stored there. The user needs to free the returned string. In case of an error and - error code is returned and NULL stored at R_PASSPHRASE. If - R_CANCEL_ALL is not NULL and the user canceled by directly closing - the window true will be stored at this address. */ + error code is returned and NULL stored at R_PASSPHRASE. */ gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, - char **r_passphrase, int *r_cancel_all) + char **r_passphrase) { gpg_error_t err; const char *text1 = prompt; @@ -316,7 +314,7 @@ agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, pi2->check_cb_arg = pi->pin; next_try: - err = agent_askpin (ctrl, text1, NULL, initial_errtext, pi, r_cancel_all); + err = agent_askpin (ctrl, text1, NULL, initial_errtext, pi); initial_errtext = NULL; if (!err) { @@ -329,7 +327,7 @@ agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, /* Unless the passphrase is empty, ask to confirm it. */ if (pi->pin && *pi->pin) { - err = agent_askpin (ctrl, text2, NULL, NULL, pi2, NULL); + err = agent_askpin (ctrl, text2, NULL, NULL, pi2); if (err == -1) { /* The re-entered one did not match and the user did not hit cancel. */ @@ -381,7 +379,7 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, rc = agent_ask_new_passphrase (ctrl, _("Please enter the passphrase to%0A" "to protect your new key"), - &passphrase, NULL); + &passphrase); if (rc) return rc; @@ -473,7 +471,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey) rc = agent_ask_new_passphrase (ctrl, _("Please enter the new passphrase"), - &passphrase, NULL); + &passphrase); if (!rc) { rc = store_key (s_skey, passphrase, 1); diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 477185c49..5cd7108cb 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -1523,6 +1523,8 @@ create_server_socket (char *name, int is_ssh, assuan_sock_nonce_t *nonce) a hang. */ if (!is_ssh && !check_for_running_agent (1, 1)) { + log_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX); + log_set_file (NULL); log_error (_("a gpg-agent is already running - " "not starting a new one\n")); *name = 0; /* Inhibit removal of the socket by cleanup(). */ diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index 11d0cc375..b9835f351 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -67,8 +67,6 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, rc = agent_key_from_file (ctrl, NULL, desc_text, ctrl->keygrip, &shadow_info, CACHE_MODE_NORMAL, NULL, &s_skey, NULL); - if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED) - rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); if (rc) { if (gpg_err_code (rc) == GPG_ERR_ENOENT) diff --git a/agent/pksign.c b/agent/pksign.c index 12f4420be..ac5f4e1a0 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -256,8 +256,6 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, rc = agent_key_from_file (ctrl, cache_nonce, desc_text, ctrl->keygrip, &shadow_info, cache_mode, lookup_ttl, &s_skey, NULL); - if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED) - rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); if (rc) { log_error ("failed to read the secret key\n"); diff --git a/agent/protect-tool.c b/agent/protect-tool.c index be0bfd2e2..c5e43a38b 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -704,7 +704,8 @@ get_passphrase (int promptno) repeat, repeat, 1, &pw); if (err) { - if (gpg_err_code (err) == GPG_ERR_CANCELED) + if (gpg_err_code (err) == GPG_ERR_CANCELED + || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) log_info (_("cancelled\n")); else log_error (_("error while asking for the passphrase: %s\n"), diff --git a/common/ChangeLog b/common/ChangeLog index 3b95f2d86..7fa83fef9 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,8 @@ +2010-10-13 Werner Koch + + * miscellaneous.c (parse_version_number, parse_version_string) + (gnupg_compare_version): New. + 2010-10-04 Werner Koch * gettime.c (asctimestamp) [W32CE]: Do not print the timezone. diff --git a/common/miscellaneous.c b/common/miscellaneous.c index 01d8c97be..fe065e653 100644 --- a/common/miscellaneous.c +++ b/common/miscellaneous.c @@ -236,3 +236,81 @@ match_multistr (const char *multistr,const char *match) } + +/* Parse the first portion of the version number S and store it at + NUMBER. On success, the function returns a pointer into S starting + with the first character, which is not part of the initial number + portion; on failure, NULL is returned. */ +static const char* +parse_version_number (const char *s, int *number) +{ + int val = 0; + + if (*s == '0' && digitp (s+1)) + return NULL; /* Leading zeros are not allowed. */ + for (; digitp (s); s++ ) + { + val *= 10; + val += *s - '0'; + } + *number = val; + return val < 0? NULL : s; +} + +/* Break up the complete string representation of the version number S, + which is expected to have this format: + + ... + + The major, minor and micro number components will be stored at + MAJOR, MINOR and MICRO. On success, a pointer to the last + component, the patch level, will be returned; on failure, NULL will + be returned. */ +static const char * +parse_version_string (const char *s, int *major, int *minor, int *micro) +{ + s = parse_version_number (s, major); + if (!s || *s != '.') + return NULL; + s++; + s = parse_version_number (s, minor); + if (!s || *s != '.') + return NULL; + s++; + s = parse_version_number (s, micro); + if (!s) + return NULL; + return s; /* Patchlevel. */ +} + +/* Return true if version string is at least version B. */ +int +gnupg_compare_version (const char *a, const char *b) +{ + int a_major, a_minor, a_micro; + int b_major, b_minor, b_micro; + const char *a_plvl, *b_plvl; + + if (!a || !b) + return 0; + + /* Parse version A. */ + a_plvl = parse_version_string (a, &a_major, &a_minor, &a_micro); + if (!a_plvl ) + return 0; /* Invalid version number. */ + + /* Parse version B. */ + b_plvl = parse_version_string (b, &b_major, &b_minor, &b_micro); + if (!b_plvl ) + return 0; /* Invalid version number. */ + + /* Compare version numbers. */ + return (a_major > b_major + || (a_major == b_major && a_minor > b_minor) + || (a_major == b_major && a_minor == b_minor + && a_micro > b_micro) + || (a_major == b_major && a_minor == b_minor + && a_micro == b_micro + && strcmp (a_plvl, b_plvl) >= 0)); +} + diff --git a/common/util.h b/common/util.h index 286cf50a2..10fac88f2 100644 --- a/common/util.h +++ b/common/util.h @@ -267,6 +267,8 @@ int is_file_compressed (const char *s, int *ret_rc); int match_multistr (const char *multistr,const char *match); +int gnupg_compare_version (const char *a, const char *b); + /*-- Simple replacement functions. */ #ifndef HAVE_TTYNAME diff --git a/doc/gpg.texi b/doc/gpg.texi index 566e6640a..5ccb5413f 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -1782,13 +1782,27 @@ Remove all entries from the @option{--group} list. Use @var{name} as the key to sign with. Note that this option overrides @option{--default-key}. +@ifset gpgtwoone +@item --try-secret-key @var{name} +@opindex try-secret-key +For hidden recipients GPG needs to know the keys to use for trial +decryption. The key set with @option{--default-key} is always tried +first, but this is often not sufficient. This option allows to set more +keys to be used for trial decryption. Although any valid user-id +specification may be used for @var{name} it makes sense to use at least +the long keyid to avoid ambiguities. Note that gpg-agent might pop up a +pinentry for a lot keys to do the trial decryption. If you want to stop +all further trial decryption you may use close-window button instead of +the cancel button. +@end ifset + @item --try-all-secrets @opindex try-all-secrets Don't look at the key ID as stored in the message but try all secret keys in turn to find the right decryption key. This option forces the behaviour as used by anonymous recipients (created by using -@option{--throw-keyids}) and might come handy in case where an encrypted -message contains a bogus key ID. +@option{--throw-keyids} or @option{--hidden-recipient}) and might come +handy in case where an encrypted message contains a bogus key ID. @item --skip-hidden-recipients @itemx --no-skip-hidden-recipients diff --git a/g10/ChangeLog b/g10/ChangeLog index a4873fe53..43ba53604 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,33 @@ +2010-10-13 Werner Koch + + * call-agent.c (start_agent): Send option agent-awareness. + (status_sc_op_failure): Take care of GPG_ERR_FULLY_CANCELED. + * passphrase.c (passphrase_get): Ditto. + * import.c (transfer_secret_keys): Ditto. + * card-util.c (write_sc_op_status): Ditto. + + * getkey.c (enum_secret_keys): Rewrite. + + * pubkey-enc.c (get_session_key): Skip keys without an encryption + capability. Handle GPG_ERR_FULLY_CANCELED. + * gpg.c: Add option --try-secret-key. + * options.h (struct opt): Add field secret_keys_to_try. + + * passphrase.c (next_to_last_passphrase): Remove. + +2010-10-12 Werner Koch + + * keygen.c (generate_subkeypair): Check availibility of secret parts. + + * keylist.c (print_card_serialno): Change to take a hexified serialno. + (list_keyblock_print): Print serialno and stub key indicators. + (list_keyblock_colon): Ditto. + + * getkey.c (have_any_secret_key): Remove. Replace all calls by + agent_probe_any_secret_key. + * gpgv.c (agent_probe_any_secret_key): New. + (agent_get_keyinfo): New. + 2010-10-08 Werner Koch * gpg.c: Add option --with-keygrip. diff --git a/g10/call-agent.c b/g10/call-agent.c index bdf4c9589..afbd9d6db 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -99,6 +99,7 @@ status_sc_op_failure (int rc) case 0: break; case GPG_ERR_CANCELED: + case GPG_ERR_FULLY_CANCELED: write_status_text (STATUS_SC_OP_FAILURE, "1"); break; case GPG_ERR_BAD_PIN: @@ -142,6 +143,11 @@ start_agent (ctrl_t ctrl, int for_card) agents. */ assuan_transact (agent_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); + /* Tell the agent about what version we are aware. This is + here used to indirectly enable GPG_ERR_FULLY_CANCELED. */ + assuan_transact (agent_ctx, "OPTION agent-awareness=2.1.0", + NULL, NULL, NULL, NULL, NULL, NULL); + } } diff --git a/g10/card-util.c b/g10/card-util.c index 1abcde894..12b229341 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -58,6 +58,7 @@ write_sc_op_status (gpg_error_t err) break; #if GNUPG_MAJOR_VERSION != 1 case GPG_ERR_CANCELED: + case GPG_ERR_FULLY_CANCELED: write_status_text (STATUS_SC_OP_FAILURE, "1"); break; case GPG_ERR_BAD_PIN: diff --git a/g10/getkey.c b/g10/getkey.c index 8d983487b..ad7e71cf7 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -568,8 +568,9 @@ leave: * first pubkey certificate which has the given name in a user_id. If * PK has the pubkey algo set, the function will only return a pubkey * with that algo. If NAMELIST is NULL, the first key is returned. - * The caller should provide storage for the PK. If RET_KB is not - * NULL the function will return the keyblock there. */ + * The caller should provide storage for the PK or pass NULL if it is + * not needed. If RET_KB is not NULL the function stores the entire + * keyblock at that address. */ static int key_byname (GETKEY_CTX *retctx, strlist_t namelist, PKT_public_key *pk, @@ -1146,12 +1147,13 @@ getkey_bynames (getkey_ctx_t *retctx, PKT_public_key *pk, } -/* Get a key by name and store it into PK. If RETCTX is not NULL - * return the search context which needs to be released by the caller - * using getkey_end. If NAME is NULL use the default key (see below). - * On success and if RET_KEYBLOCK is not NULL the found keyblock is - * stored at this address. WANT_SECRET passed as true requires that a - * secret key is available for the selected key. +/* Get a key by name and store it into PK if that is not NULL. If + * RETCTX is not NULL return the search context which needs to be + * released by the caller using getkey_end. If NAME is NULL use the + * default key (see below). On success and if RET_KEYBLOCK is not + * NULL the found keyblock is stored at this address. WANT_SECRET + * passed as true requires that a secret key is available for the + * selected key. * * If WANT_SECRET is true and NAME is NULL and a default key has been * defined that defined key is used. In all other cases the first @@ -2459,7 +2461,7 @@ lookup (getkey_ctx_t ctx, kbnode_t *ret_keyblock, int want_secret) goto skip; } - if (want_secret && !have_any_secret_key (NULL, ctx->keyblock)) + if (want_secret && agent_probe_any_secret_key (NULL, ctx->keyblock)) goto skip; /* No secret key available. */ /* Warning: node flag bits 0 and 1 should be preserved by @@ -2504,57 +2506,42 @@ found: -/**************** - * FIXME: Replace by the generic function - * It does not work as it is right now - it is used at - * one place: to get the key for an anonymous recipient. - * - * set with_subkeys true to include subkeys - * set with_spm true to include secret-parts-missing keys - * - * Enumerate all primary secret keys. Caller must use these procedure: +/* + * Enumerate certain secret keys. Caller must use these procedure: * 1) create a void pointer and initialize it to NULL * 2) pass this void pointer by reference to this function * and provide space for the secret key (pass a buffer for sk) - * 3) call this function as long as it does not return -1 - * to indicate EOF. + * 3) call this function as long as it does not return an error. + * The error code GPG_ERR_EOF indicates the end of the listing. * 4) Always call this function a last time with SK set to NULL, * so that can free it's context. */ -int -enum_secret_keys (void **context, PKT_public_key * sk, - int with_subkeys, int with_spm) +gpg_error_t +enum_secret_keys (void **context, PKT_public_key *sk) { - log_debug ("FIXME: Anonymous recipient does not yet work\n"); - return -1; -#if 0 - - int rc = 0; + gpg_error_t err = 0; + const char *name; struct { int eof; - int first; - KEYDB_HANDLE hd; - KBNODE keyblock; - KBNODE node; + int state; + strlist_t sl; + kbnode_t keyblock; + kbnode_t node; } *c = *context; - if (!c) { /* Make a new context. */ - c = xmalloc_clear (sizeof *c); + c = xtrycalloc (1, sizeof *c); + if (!c) + return gpg_error_from_syserror (); *context = c; - c->hd = keydb_new (1); /*FIXME*/ - c->first = 1; - c->keyblock = NULL; - c->node = NULL; } if (!sk) { /* Free the context. */ - keydb_release (c->hd); release_kbnode (c->keyblock); xfree (c); *context = NULL; @@ -2562,48 +2549,79 @@ enum_secret_keys (void **context, PKT_public_key * sk, } if (c->eof) - return -1; + return gpg_error (GPG_ERR_EOF); - do + for (;;) { - /* Get the next secret key from the current keyblock. */ + /* Loop until we have a keyblock. */ + while (!c->keyblock) + { + /* Loop over the list of secret keys. */ + do + { + name = NULL; + switch (c->state) + { + case 0: /* First try to use the --default-key. */ + if (opt.def_secret_key && *opt.def_secret_key) + name = opt.def_secret_key; + c->state = 1; + break; + + case 1: /* Init list of keys to try. */ + c->sl = opt.secret_keys_to_try; + c->state++; + break; + + case 2: /* Get next item from list. */ + if (c->sl) + { + name = c->sl->d; + c->sl = c->sl->next; + } + else + c->state++; + break; + + default: /* No more names to check - stop. */ + c->eof = 1; + return gpg_error (GPG_ERR_EOF); + } + } + while (!name || !*name); + + err = getkey_byname (NULL, NULL, name, 1, &c->keyblock); + if (err) + { + /* getkey_byname might return a keyblock even in the + error case - I have not checked. Thus better release + it. */ + release_kbnode (c->keyblock); + c->keyblock = NULL; + } + else + c->node = c->keyblock; + } + + /* Get the next key from the current keyblock. */ for (; c->node; c->node = c->node->next) { - if ((c->node->pkt->pkttype == PKT_SECRET_KEY - || (with_subkeys - && c->node->pkt->pkttype == PKT_SECRET_SUBKEY)) - && !(c->node->pkt->pkt.secret_key->protect.s2k.mode == 1001 - && !with_spm)) + if (c->node->pkt->pkttype == PKT_PUBLIC_KEY + || c->node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { - copy_secret_key (sk, c->node->pkt->pkt.secret_key); + copy_public_key (sk, c->node->pkt->pkt.public_key); c->node = c->node->next; return 0; /* Found. */ } - } + } + + /* Dispose the keyblock and continue. */ release_kbnode (c->keyblock); - c->keyblock = c->node = NULL; - - rc = c->first ? keydb_search_first (c->hd) : keydb_search_next (c->hd); - c->first = 0; - if (rc) - { - keydb_release (c->hd); - c->hd = NULL; - c->eof = 1; - return -1; /* eof */ - } - - rc = keydb_get_keyblock (c->hd, &c->keyblock); - c->node = c->keyblock; + c->keyblock = NULL; } - while (!rc); - - return rc; /* Error. */ -#endif } + - - /********************************************* *********** User ID printing helpers ******* *********************************************/ diff --git a/g10/gpg.c b/g10/gpg.c index c25c3ba18..5ee03a63b 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -177,6 +177,7 @@ enum cmd_and_opt_values oDefRecipient, oDefRecipientSelf, oNoDefRecipient, + oTrySecretKey, oOptions, oDebug, oDebugLevel, @@ -460,6 +461,8 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oLocalUser, "local-user", N_("|USER-ID|use USER-ID to sign or decrypt")), + ARGPARSE_s_s (oTrySecretKey, "try-secret-key", "@"), + ARGPARSE_s_i (oCompress, NULL, N_("|N|set compress level to N (0 disables)")), ARGPARSE_s_i (oCompressLevel, "compress-level", "@"), @@ -1622,6 +1625,7 @@ gpgconf_list (const char *configfile) es_printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("default-key:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("encrypt-to:%lu:\n", GC_OPT_FLAG_NONE); + es_printf ("try-secret-key:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("auto-key-locate:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT); @@ -2526,6 +2530,12 @@ main (int argc, char **argv) sl->flags = 2; any_explicit_recipient = 1; break; + + case oTrySecretKey: + add_to_strlist2 (&opt.secret_keys_to_try, + pargs.r.ret_str, utf8_strings); + break; + case oTextmodeShort: opt.textmode = 2; break; case oTextmode: opt.textmode=1; break; case oNoTextmode: opt.textmode=0; break; diff --git a/g10/gpgv.c b/g10/gpgv.c index b342d2481..569601e89 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -546,3 +546,21 @@ agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) (void)pk; return gpg_error (GPG_ERR_NO_SECKEY); } + +gpg_error_t +agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) +{ + (void)ctrl; + (void)keyblock; + return gpg_error (GPG_ERR_NO_SECKEY); +} + +gpg_error_t +agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno) +{ + (void)ctrl; + (void)hexkeygrip; + *r_serialno = NULL; + return gpg_error (GPG_ERR_NO_SECKEY); +} + diff --git a/g10/import.c b/g10/import.c index b3969770f..31160c33e 100644 --- a/g10/import.c +++ b/g10/import.c @@ -362,7 +362,7 @@ import_print_stats (void *hd) * Read the next keyblock from stream A. * PENDING_PKT should be initialzed to NULL * and not chnaged form the caller. - * Retunr: 0 = okay, -1 no more blocks or another errorcode. + * Return: 0 = okay, -1 no more blocks or another errorcode. */ static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root ) @@ -1142,8 +1142,15 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) stats->count++; stats->secret_read++; - /* For now we ignore the stub keys becuase we don't have real - support for them in gpg-agent. */ + /* We ignore stub keys. The way we handle them in other parts + of the code is by asking the agent whether any secret key is + available for a given keyblock and then concluding that we + have a secret key; all secret (sub)keys of the keyblock the + agent does not know of are then stub keys. This works also + for card stub keys. The learn command or the card-status + command may be used to check with the agent whether a card + has been inserted and a stub key is in turn generated by the + agent. */ if (ski->s2k.mode == 1001 || ski->s2k.mode == 1002) continue; @@ -1288,7 +1295,8 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock) write_status (STATUS_RSA_OR_IDEA); idea_cipher_warn (0); } - if (gpg_err_code (err) == GPG_ERR_CANCELED) + if (gpg_err_code (err) == GPG_ERR_CANCELED + || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) break; /* Don't try the other subkeys. */ } } diff --git a/g10/keydb.h b/g10/keydb.h index eab59e0a5..9ec2496c5 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -241,11 +241,8 @@ gpg_error_t getkey_next (getkey_ctx_t ctx, PKT_public_key *pk, kbnode_t *ret_keyblock); void getkey_end (getkey_ctx_t ctx); -int have_any_secret_key (ctrl_t ctrl, kbnode_t keyblock); +gpg_error_t enum_secret_keys (void **context, PKT_public_key *pk); - -//int enum_secret_keys( void **context, PKT_secret_key *sk, -// int with_subkeys, int with_spm ); void merge_keys_and_selfsig( KBNODE keyblock ); char*get_user_id_string( u32 *keyid ); char*get_user_id_string_native( u32 *keyid ); diff --git a/g10/keyedit.c b/g10/keyedit.c index d1cacaf39..5b4c60a0d 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1638,7 +1638,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, /* See whether we have a matching secret key. */ if (seckey_check) { - have_seckey = have_any_secret_key (ctrl, keyblock); + have_seckey = !agent_probe_any_secret_key (ctrl, keyblock); if (have_seckey && !quiet) tty_printf (_("Secret key is available.\n")); } diff --git a/g10/keygen.c b/g10/keygen.c index 00ad26ecb..16a7f0b8d 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -3441,6 +3441,8 @@ generate_subkeypair (KBNODE keyblock) u32 expire; unsigned int nbits; u32 cur_time; + char *hexgrip = NULL; + char *serialno = NULL; /* Break out the primary key. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); @@ -3476,37 +3478,16 @@ generate_subkeypair (KBNODE keyblock) goto leave; } -#warning ask gpg-agent on the availibility of the secret key - /* if (pri_sk->is_protected && pri_sk->protect.s2k.mode == 1001) */ - /* { */ - /* tty_printf (_("Secret parts of primary key are not available.\n")); */ - /* err = G10ERR_NO_SECKEY; */ - /* goto leave; */ - /* } */ - - - /* /\* Unprotect to get the passphrase. *\/ */ - /* switch (is_secret_key_protected (pri_sk) ) */ - /* { */ - /* case -1: */ - /* err = G10ERR_PUBKEY_ALGO; */ - /* break; */ - /* case 0: */ - /* tty_printf (_("This key is not protected.\n")); */ - /* break; */ - /* case -2: */ - /* tty_printf (_("Secret parts of primary key are stored on-card.\n")); */ - /* ask_pass = 1; */ - /* break; */ - /* default: */ - /* tty_printf (_("Key is protected.\n")); */ - /* err = check_secret_key ( pri_sk, 0 ); */ - /* if (!err) */ - /* passphrase = get_last_passphrase(); */ - /* break; */ - /* } */ - /* if (err) */ - /* goto leave; */ + err = hexkeygrip_from_pk (pri_psk, &hexgrip); + if (err) + goto leave; + if (agent_get_keyinfo (NULL, hexgrip, &serialno)) + { + tty_printf (_("Secret parts of primary key are not available.\n")); + goto leave; + } + if (serialno) + tty_printf (_("Secret parts of primary key are stored on-card.\n")); algo = ask_algo (1, NULL, &use); assert (algo); @@ -3536,6 +3517,8 @@ generate_subkeypair (KBNODE keyblock) write_status_text (STATUS_KEY_CREATED, "S"); leave: + xfree (hexgrip); + xfree (serialno); if (err) log_error (_("Key generation failed: %s\n"), g10_errstr (err) ); return err; diff --git a/g10/keylist.c b/g10/keylist.c index d144afc39..e8440f77b 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -40,11 +40,12 @@ #include "main.h" #include "i18n.h" #include "status.h" +#include "call-agent.h" static void list_all (int); static void list_one (strlist_t names, int secret); static void locate_one (ctrl_t ctrl, strlist_t names); -static void print_card_serialno (PKT_public_key *sk); +static void print_card_serialno (const char *serialno); struct sig_stats { @@ -175,6 +176,7 @@ print_pubkey_info (estream_t fp, PKT_public_key * pk) /* Print basic information of a secret key including the card serial number information. */ +#ifdef ENABLE_CARD_SUPPORT void print_card_key_info (estream_t fp, kbnode_t keyblock) { @@ -224,7 +226,7 @@ print_card_key_info (estream_t fp, kbnode_t keyblock) /* } */ /* } */ } - +#endif /*ENABLE_CARD_SUPPORT*/ /* Flags = 0x01 hashed 0x02 critical. */ @@ -444,7 +446,7 @@ list_all (int secret) log_error ("keydb_get_keyblock failed: %s\n", g10_errstr (rc)); goto leave; } - if (secret && !have_any_secret_key (NULL, keyblock)) + if (secret && agent_probe_any_secret_key (NULL, keyblock)) ; /* Secret key listing requested but this isn't one. */ else { @@ -757,13 +759,15 @@ dump_attribs (const PKT_user_id *uid, PKT_public_key *pk) static void list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque) { - int rc = 0; + int rc; KBNODE kbctx; KBNODE node; PKT_public_key *pk; struct sig_stats *stats = opaque; int skip_sigs = 0; int s2k_char; + char *hexgrip = NULL; + char *serialno = NULL; /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); @@ -775,10 +779,23 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque) } pk = node->pkt->pkt.public_key; - - /* Fixme: Get s2k mode from the agent. */ - s2k_char = (/*(sk->protect.s2k.mode == 1001)? '#' : - (sk->protect.s2k.mode == 1002)? '>' : */' '); + + if (secret || opt.with_keygrip) + { + rc = hexkeygrip_from_pk (pk, &hexgrip); + if (rc) + log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); + } + + if (secret) + { + if (!agent_get_keyinfo (NULL, hexgrip, &serialno)) + s2k_char = serialno? '>':' '; + else + s2k_char = '#'; /* Key not found. */ + } + else + s2k_char = ' '; check_trustdb_stale (); @@ -822,19 +839,11 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque) if (fpr) print_fingerprint (pk, 0); - if (opt.with_keygrip) - { - char *p; + if (opt.with_keygrip && hexgrip) + es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); - if (!hexkeygrip_from_pk (pk, &p)) - { - es_fprintf (es_stdout, " Keygrip = %s\n", p); - xfree (p); - } - } - - /* FIXME: Change this function to take a PK and ask the agent: */ - /* if (secret) print_card_serialno (sk); */ + if (serialno) + print_card_serialno (serialno); if (opt.with_key_data) print_key_data (pk); @@ -895,10 +904,25 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque) else skip_sigs = 0; - /* Fixme: Get s2k mode from the agent. */ - s2k_char = (/*(sk->protect.s2k.mode == 1001)? '#' : - (sk->protect.s2k.mode == 1002)? '>' : */' '); - + xfree (serialno); serialno = NULL; + xfree (hexgrip); hexgrip = NULL; + if (secret || opt.with_keygrip) + { + rc = hexkeygrip_from_pk (pk2, &hexgrip); + if (rc) + log_error ("error computing a keygrip: %s\n", + gpg_strerror (rc)); + } + if (secret) + { + if (!agent_get_keyinfo (NULL, hexgrip, &serialno)) + s2k_char = serialno? '>':' '; + else + s2k_char = '#'; /* Key not found. */ + } + else + s2k_char = ' '; + es_fprintf (es_stdout, "%s%c %4u%c/%s %s", secret? "ssb":"sub", s2k_char, @@ -926,20 +950,11 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque) if (fpr > 1) { print_fingerprint (pk2, 0); - /* FIXME: (see above) */ - /* if (secret) */ - /* print_card_serialno (sk2); */ - } - if (opt.with_keygrip) - { - char *p; - - if (!hexkeygrip_from_pk (pk2, &p)) - { - es_fprintf (es_stdout, " Keygrip = %s\n", p); - xfree (p); - } + if (serialno) + print_card_serialno (serialno); } + if (opt.with_keygrip && hexgrip) + es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); if (opt.with_key_data) print_key_data (pk2); } @@ -1050,6 +1065,8 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque) } } es_putc ('\n', es_stdout); + xfree (serialno); + xfree (hexgrip); } void @@ -1079,7 +1096,7 @@ print_revokers (PKT_public_key * pk) static void list_keyblock_colon (KBNODE keyblock, int secret, int fpr) { - int rc = 0; + int rc; KBNODE kbctx; KBNODE node; PKT_public_key *pk; @@ -1088,6 +1105,9 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr) int ulti_hack = 0; int i; char *p; + char *hexgrip = NULL; + char *serialno = NULL; + int stubkey; /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); @@ -1099,6 +1119,15 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr) } pk = node->pkt->pkt.public_key; + if (secret || opt.with_keygrip || opt.with_key_data) + { + rc = hexkeygrip_from_pk (pk, &hexgrip); + if (rc) + log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); + } + stubkey = 0; + if (secret && agent_get_keyinfo (NULL, hexgrip, &serialno)) + stubkey = 1; /* Key not found. */ keyid_from_pk (pk, keyid); es_fputs (secret? "sec:":"pub:", es_stdout); @@ -1135,16 +1164,10 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr) { es_putc (':', es_stdout); /* End of field 13. */ es_putc (':', es_stdout); /* End of field 14. */ - if (/*FIXME sk->protect.s2k.mode*/1 == 1001) - es_putc ('#', es_stdout); /* Key is just a stub. */ - else if (/*FIXME sk->protect.s2k.mode*/1 == 1002) - { - /* Key is stored on an external token (card) or handled by - the gpg-agent. Print the serial number of that token - here. */ - /* FIXME: for (i = 0; i < sk->protect.ivlen; i++) */ - /* es_fprintf (es_stdout, "%02X", sk->protect.iv[i]); */ - } + if (stubkey) + es_putc ('#', es_stdout); + else if (serialno) + es_fputs(serialno, es_stdout); es_putc (':', es_stdout); /* End of field 15. */ } es_putc ('\n', es_stdout); @@ -1154,11 +1177,8 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr) print_fingerprint (pk, 0); if (opt.with_key_data || opt.with_keygrip) { - if (!hexkeygrip_from_pk (pk, &p)) - { - es_fprintf (es_stdout, "grp:::::::::%s:\n", p); - xfree (p); - } + if (hexgrip) + es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip); if (opt.with_key_data) print_key_data (pk); } @@ -1213,7 +1233,21 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr) else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 keyid2[2]; - PKT_public_key *pk2 = node->pkt->pkt.public_key; + PKT_public_key *pk2; + + pk2 = node->pkt->pkt.public_key; + xfree (hexgrip); hexgrip = NULL; + xfree (serialno); serialno = NULL; + if (secret || opt.with_keygrip || opt.with_key_data) + { + rc = hexkeygrip_from_pk (pk2, &hexgrip); + if (rc) + log_error ("error computing a keygrip: %s\n", + gpg_strerror (rc)); + } + stubkey = 0; + if (secret && agent_get_keyinfo (NULL, hexgrip, &serialno)) + stubkey = 1; /* Key not found. */ keyid_from_pk (pk2, keyid2); es_fputs (secret? "ssb:":"sub:", es_stdout); @@ -1243,16 +1277,10 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr) { es_putc (':', es_stdout); /* End of field 13. */ es_putc (':', es_stdout); /* End of field 14. */ - if (/*FIXME:sk2->protect.s2k.mode*/1 == 1001) - es_putc ('#', es_stdout); /* Key is just a stub. */ - else if (/*FIXME: sk2->protect.s2k.mode*/1 == 1002) - { - /* Key is stored on an external token (card) or - handled by the gpg-agent. Print the serial - number of that token here. */ - /* FIXME: for (i = 0; i < sk2->protect.ivlen; i++) - es_fprintf (es_stdout, "%02X", sk2->protect.iv[i]); */ - } + if (stubkey) + es_putc ('#', es_stdout); + else if (serialno) + es_fputs (serialno, es_stdout); es_putc (':', es_stdout); /* End of field 15. */ } es_putc ('\n', es_stdout); @@ -1260,11 +1288,8 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr) print_fingerprint (pk2, 0); if (opt.with_key_data || opt.with_keygrip) { - if (!hexkeygrip_from_pk (pk2, &p)) - { - es_fprintf (es_stdout, "grp:::::::::%s:\n", p); - xfree (p); - } + if (hexgrip) + es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip); if (opt.with_key_data) print_key_data (pk2); } @@ -1385,6 +1410,9 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr) /* fixme: check or list other sigs here */ } } + + xfree (hexgrip); + xfree (serialno); } /* @@ -1550,38 +1578,25 @@ print_fingerprint (PKT_public_key *pk, int mode) /* Print the serial number of an OpenPGP card if available. */ static void -print_card_serialno (PKT_public_key *pk) +print_card_serialno (const char *serialno) { - log_debug ("Fixme: Needs to be adjusted to gpg-agent\n"); - /* int i; */ + if (!serialno) + return; + if (opt.with_colons) + return; /* Handled elsewhere. */ - /* if (!sk) */ - /* return; */ - /* if (!sk->is_protected || sk->protect.s2k.mode != 1002) */ - /* return; /\* Not a card. *\/ */ - /* if (opt.with_colons) */ - /* return; /\* Handled elsewhere. *\/ */ - - /* es_fputs (_(" Card serial no. ="), es_stdout); */ - /* es_putc (' ', es_stdout); */ - /* if (sk->protect.ivlen == 16 */ - /* && !memcmp (sk->protect.iv, "\xD2\x76\x00\x01\x24\x01", 6)) */ - /* { */ - /* /\* This is an OpenPGP card. Just print the relevant part. *\/ */ - /* for (i = 8; i < 14; i++) */ - /* { */ - /* if (i == 10) */ - /* es_putc (' ', es_stdout); */ - /* es_fprintf (es_stdout, "%02X", sk->protect.iv[i]); */ - /* } */ - /* } */ - /* else */ - /* { */ - /* /\* Something is wrong: Print all. *\/ */ - /* for (i = 0; i < sk->protect.ivlen; i++) */ - /* es_fprintf (es_stdout, "%02X", sk->protect.iv[i]); */ - /* } */ - /* es_putc ('\n', es_stdout); */ + es_fputs (_(" Card serial no. ="), es_stdout); + es_putc (' ', es_stdout); + if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) + { + /* This is an OpenPGP card. Print the relevant part. */ + /* Example: D2760001240101010001000003470000 */ + /* xxxxyyyyyyyy */ + es_fprintf (es_stdout, "%.*s %.*s", 4, serialno+16, 8, serialno+20); + } + else + es_fputs (serialno, es_stdout); + es_putc ('\n', es_stdout); } diff --git a/g10/misc.c b/g10/misc.c index 91d1c310a..e3bebdd20 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -555,11 +555,10 @@ get_signature_count (PKT_public_key *pk) /* if(agent_scd_getattr("SIG-COUNTER",&info)==0) */ /* return info.sig_counter; */ /* } */ -#endif - - /* How to do this without a card? */ - +#else + (void)pk; return 0; +#endif } /* Expand %-strings. Returns a string which must be xfreed. Returns diff --git a/g10/options.h b/g10/options.h index 499428e92..b71271da6 100644 --- a/g10/options.h +++ b/g10/options.h @@ -79,6 +79,8 @@ struct const char *def_secret_key; char *def_recipient; int def_recipient_self; + strlist_t secret_keys_to_try; + int def_cert_level; int min_cert_level; int ask_cert_level; diff --git a/g10/passphrase.c b/g10/passphrase.c index 60560123f..b28477fd5 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -211,17 +211,6 @@ get_last_passphrase() return p; } -/* As if we had used the passphrase - make it the last_pw. */ -void -next_to_last_passphrase(void) -{ - if (next_pw) - { - last_pw=next_pw; - next_pw=NULL; - } -} - /* Here's an interesting question: since this passphrase was passed in on the command line, is there really any point in using secure memory for it? I'm going with 'yes', since it doesn't hurt, and @@ -407,7 +396,8 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat, if (!rc) ; - else if ( gpg_err_code (rc) == GPG_ERR_CANCELED ) + else if (gpg_err_code (rc) == GPG_ERR_CANCELED + || gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED) { log_info (_("cancelled by user\n") ); if (canceled) diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 74bdc7655..770e4fb67 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -94,14 +94,13 @@ get_session_key (PKT_pubkey_enc * k, DEK * dek) { void *enum_context = NULL; u32 keyid[2]; - char *p; for (;;) { if (sk) free_public_key (sk); sk = xmalloc_clear (sizeof *sk); - rc = -1; /* FIXME:enum_secret_keys (&enum_context, sk, 1, 0);*/ + rc = enum_secret_keys (&enum_context, sk); if (rc) { rc = G10ERR_NO_SECKEY; @@ -109,42 +108,22 @@ get_session_key (PKT_pubkey_enc * k, DEK * dek) } if (sk->pubkey_algo != k->pubkey_algo) continue; + if (!(sk->pubkey_usage & PUBKEY_USAGE_ENC)) + continue; keyid_from_pk (sk, keyid); log_info (_("anonymous recipient; trying secret key %s ...\n"), keystr (keyid)); - if (!opt.try_all_secrets && !is_status_enabled ()) - { - p = get_last_passphrase (); - set_next_passphrase (p); - xfree (p); - } - - /* rc = check_secret_key( sk, opt.try_all_secrets?1:-1 ); /\* ask */ - /* only */ - /* once *\/ */ - /* if( !rc ) */ - { - rc = get_it (k, dek, sk, keyid); - /* Successfully checked the secret key (either it was a - card, had no passphrase, or had the right passphrase) - but couldn't decrypt the session key, so thus that key - is not the anonymous recipient. Move the next - passphrase into last for the next round. We only do - this if the secret key was successfully checked as in - the normal case, check_secret_key handles this for us - via passphrase_to_dek. */ - if (rc) - next_to_last_passphrase (); - } - + rc = get_it (k, dek, sk, keyid); if (!rc) { log_info (_("okay, we are the anonymous recipient.\n")); break; } + else if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED) + break; /* Don't try any more secret keys. */ } - enum_secret_keys (&enum_context, NULL, 0, 0); /* free context */ + enum_secret_keys (&enum_context, NULL); /* free context */ } leave: