summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--g10/call-agent.c101
-rw-r--r--g10/getkey.c21
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)