agent: Avoid multiple calls to scd for KEYINFO.

* agent/command.c (struct server_local_s): Add last_card_keyinfo.
(eventcounter): Add maybe_key_change.
(cmd_genkey, cmd_scd, cmd_import_key, cmd_delete_key): Bump new
counter.
(cmd_keyinfo): Cache the keyinfo from the card.
(start_command_handler): Release the cache.
--

This cache speeds up processing of commands like "gpg -K" because
scdaemon does not need to be asked for each key as long as nothing
changed with the card.

We should have a better notification service from scdaemon to make
sure that we get only the relevant events.  What we do right now is a
bit course but sufficient.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-01-13 16:27:12 +01:00
parent aaef0fc3a7
commit 2e86cca7f4
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
1 changed files with 45 additions and 2 deletions

View File

@ -112,6 +112,16 @@ struct server_local_s
/* Last PASSWD_NONCE sent as status (malloced). */
char *last_passwd_nonce;
/* Per connection cache of the keyinfo from the cards. The
* eventcounters for cards at the time the info was fetched is
* stored here as a freshness indicator. */
struct {
struct card_key_info_s *ki;
unsigned int eventno;
unsigned int maybe_key_change;
} last_card_keyinfo;
};
@ -148,6 +158,11 @@ struct
detected. */
unsigned int card;
/* Internal counter to track possible changes to a key.
* FIXME: This should be replaced by generic notifications from scd.
*/
unsigned int maybe_key_change;
} eventcounter;
@ -927,6 +942,8 @@ cmd_genkey (assuan_context_t ctx, char *line)
if (*line)
cache_nonce = xtrystrdup (line);
eventcounter.maybe_key_change++;
/* First inquire the parameters */
rc = print_assuan_status (ctx, "INQUIRE_MAXLEN", "%u", MAXLEN_KEYPARAM);
if (!rc)
@ -1307,7 +1324,24 @@ cmd_keyinfo (assuan_context_t ctx, char *line)
if (opt_with_ssh || list_mode == 2)
cf = ssh_open_control_file ();
agent_card_keyinfo (ctrl, NULL, 0, &keyinfo_on_cards);
/* Take the keyinfo for cards from our local cache. Actually this
* cache could be a global one but then we would need to employ
* reference counting. */
if (ctrl->server_local->last_card_keyinfo.ki
&& ctrl->server_local->last_card_keyinfo.eventno == eventcounter.card
&& (ctrl->server_local->last_card_keyinfo.maybe_key_change
== eventcounter.maybe_key_change))
{
keyinfo_on_cards = ctrl->server_local->last_card_keyinfo.ki;
}
else if (!agent_card_keyinfo (ctrl, NULL, 0, &keyinfo_on_cards))
{
agent_card_free_keyinfo (ctrl->server_local->last_card_keyinfo.ki);
ctrl->server_local->last_card_keyinfo.ki = keyinfo_on_cards;
ctrl->server_local->last_card_keyinfo.eventno = eventcounter.card;
ctrl->server_local->last_card_keyinfo.maybe_key_change
= eventcounter.maybe_key_change;
}
if (list_mode == 2)
{
@ -1413,7 +1447,6 @@ cmd_keyinfo (assuan_context_t ctx, char *line)
}
leave:
agent_card_free_keyinfo (keyinfo_on_cards);
ssh_close_control_file (cf);
if (dir)
closedir (dir);
@ -2034,6 +2067,9 @@ cmd_scd (assuan_context_t ctx, char *line)
if (ctrl->restricted)
return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
/* All SCD prefixed commands may change a key. */
eventcounter.maybe_key_change++;
rc = divert_generic_cmd (ctrl, line, ctx);
#else
(void)ctx; (void)line;
@ -2152,6 +2188,8 @@ cmd_import_key (assuan_context_t ctx, char *line)
if (*line)
cache_nonce = xtrystrdup (line);
eventcounter.maybe_key_change++;
assuan_begin_confidential (ctx);
err = assuan_inquire (ctx, "KEYDATA",
&wrappedkey, &wrappedkeylen, MAXLEN_KEYDATA);
@ -2495,6 +2533,8 @@ cmd_delete_key (assuan_context_t ctx, char *line)
stub_only = has_option (line, "--stub-only");
line = skip_options (line);
eventcounter.maybe_key_change++;
/* If the use of a loopback pinentry has been disabled, we assume
* that a silent deletion of keys shall also not be allowed. */
if (!opt.allow_loopback_pinentry)
@ -3640,6 +3680,9 @@ start_command_handler (ctrl_t ctrl, gnupg_fd_t listen_fd, gnupg_fd_t fd)
}
}
/* Clear the keyinfo cache. */
agent_card_free_keyinfo (ctrl->server_local->last_card_keyinfo.ki);
/* Reset the nonce caches. */
clear_nonce_cache (ctrl);