diff options
-rw-r--r-- | g10/call-agent.c | 101 | ||||
-rw-r--r-- | g10/getkey.c | 21 |
2 files changed, 80 insertions, 42 deletions
diff --git a/g10/call-agent.c b/g10/call-agent.c index f4ee3cdae..5a877c38f 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1927,8 +1927,52 @@ agent_get_s2k_count (void) +struct keyinfo_data_parm_s +{ + char *serialno; + int is_smartcard; + int passphrase_cached; + int cleartext; + int card_available; +}; + + +static gpg_error_t +keyinfo_status_cb (void *opaque, const char *line) +{ + struct keyinfo_data_parm_s *data = opaque; + char *s; + + if ((s = has_leading_keyword (line, "KEYINFO")) && data) + { + /* Parse the arguments: + * 0 1 2 3 4 5 + * <keygrip> <type> <serialno> <idstr> <cached> <protection> + * + * 6 7 8 + * <sshfpr> <ttl> <flags> + */ + char *fields[9]; + + if (split_fields (s, fields, DIM (fields)) == 9) + { + data->is_smartcard = (fields[1][0] == 'T'); + if (data->is_smartcard && !data->serialno && strcmp (fields[2], "-")) + data->serialno = xtrystrdup (fields[2]); + /* '1' for cached */ + data->passphrase_cached = (fields[4][0] == '1'); + /* 'P' for protected, 'C' for clear */ + data->cleartext = (fields[5][0] == 'C'); + /* 'A' for card is available */ + data->card_available = (fields[8][0] == 'A'); + } + } + return 0; +} + + /* Ask the agent whether a secret key for the given public key is - available. Returns 0 if not available. */ + available. Returns 0 if not available. Bigger value is preferred. */ int agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { @@ -1936,6 +1980,10 @@ agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) char line[ASSUAN_LINELENGTH]; char *hexgrip; + struct keyinfo_data_parm_s keyinfo; + + memset (&keyinfo, 0, sizeof keyinfo); + err = start_agent (ctrl, 0); if (err) return err; @@ -1944,12 +1992,24 @@ agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) if (err) return err; - snprintf (line, sizeof line, "HAVEKEY %s", hexgrip); + snprintf (line, sizeof line, "KEYINFO %s", hexgrip); xfree (hexgrip); - err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, + keyinfo_status_cb, &keyinfo); + xfree (keyinfo.serialno); if (err) return 0; + + if (keyinfo.card_available) + return 4; + + if (keyinfo.passphrase_cached) + return 3; + + if (keyinfo.is_smartcard) + return 2; + return 1; } @@ -2006,41 +2066,6 @@ agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) -struct keyinfo_data_parm_s -{ - char *serialno; - int cleartext; -}; - - -static gpg_error_t -keyinfo_status_cb (void *opaque, const char *line) -{ - struct keyinfo_data_parm_s *data = opaque; - int is_smartcard; - char *s; - - if ((s = has_leading_keyword (line, "KEYINFO")) && data) - { - /* Parse the arguments: - * 0 1 2 3 4 5 - * <keygrip> <type> <serialno> <idstr> <cached> <protection> - */ - char *fields[6]; - - if (split_fields (s, fields, DIM (fields)) == 6) - { - is_smartcard = (fields[1][0] == 'T'); - if (is_smartcard && !data->serialno && strcmp (fields[2], "-")) - data->serialno = xtrystrdup (fields[2]); - /* 'P' for protected, 'C' for clear */ - data->cleartext = (fields[5][0] == 'C'); - } - } - return 0; -} - - /* Return the serial number for a secret key. If the returned serial number is NULL, the key is not stored on a smartcard. Caller needs to free R_SERIALNO. diff --git a/g10/getkey.c b/g10/getkey.c index 2e1c79a1a..abd44d983 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -3470,6 +3470,7 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, kbnode_t nextk; int n_subkeys = 0; int n_revoked_or_expired = 0; + int last_secret_key_avail = 0; /* Either start a loop or check just this one subkey. */ for (k = foundk ? foundk : keyblock; k; k = nextk) @@ -3527,11 +3528,23 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, continue; } - if (want_secret && !agent_probe_secret_key (NULL, pk)) + if (want_secret) { - if (DBG_LOOKUP) - log_debug ("\tno secret key\n"); - continue; + int secret_key_avail = agent_probe_secret_key (NULL, pk); + + if (!secret_key_avail) + { + if (DBG_LOOKUP) + log_debug ("\tno secret key\n"); + continue; + } + + if (secret_key_avail > last_secret_key_avail) + { + /* Use this key. */ + last_secret_key_avail = secret_key_avail; + latest_date = 0; + } } if (DBG_LOOKUP) |