aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/agent.h3
-rw-r--r--agent/call-scd.c91
-rw-r--r--agent/command-ssh.c2
-rw-r--r--agent/command.c2
-rw-r--r--agent/divert-scd.c22
-rw-r--r--agent/findkey.c2
-rw-r--r--agent/learncard.c2
-rw-r--r--agent/pksign.c80
8 files changed, 172 insertions, 32 deletions
diff --git a/agent/agent.h b/agent/agent.h
index f7e96fcff..18d60fb36 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -607,7 +607,8 @@ int agent_card_pkdecrypt (ctrl_t ctrl,
char **r_buf, size_t *r_buflen, int *r_padding);
int agent_card_readcert (ctrl_t ctrl,
const char *id, char **r_buf, size_t *r_buflen);
-int agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf);
+int agent_card_readkey (ctrl_t ctrl, const char *id,
+ unsigned char **r_buf, char **r_keyref);
gpg_error_t agent_card_writekey (ctrl_t ctrl, int force, const char *serialno,
const char *keyref,
const char *keydata, size_t keydatalen,
diff --git a/agent/call-scd.c b/agent/call-scd.c
index 347160a40..60365c980 100644
--- a/agent/call-scd.c
+++ b/agent/call-scd.c
@@ -686,7 +686,8 @@ handle_pincache_put (const char *args)
}
-/* This status callback is to intercept the PINCACHE_PUT status messages. */
+/* This status callback is to intercept the PINCACHE_PUT status
+ * messages. OPAQUE is not used. */
static gpg_error_t
pincache_put_cb (void *opaque, const char *line)
{
@@ -840,8 +841,11 @@ get_serialno_cb (void *opaque, const char *line)
return err;
}
+
/* Return the serial number of the card or an appropriate error. The
- serial number is returned as a hexstring. */
+ * serial number is returned as a hexstring. If the serial number is
+ * not required by the caller R_SERIALNO can be NULL; this might be
+ * useful to test whether a card is available. */
int
agent_card_serialno (ctrl_t ctrl, char **r_serialno, const char *demand)
{
@@ -866,7 +870,10 @@ agent_card_serialno (ctrl_t ctrl, char **r_serialno, const char *demand)
xfree (serialno);
return unlock_scd (ctrl, rc);
}
- *r_serialno = serialno;
+ if (r_serialno)
+ *r_serialno = serialno;
+ else
+ xfree (serialno);
return unlock_scd (ctrl, 0);
}
@@ -1165,41 +1172,107 @@ agent_card_readcert (ctrl_t ctrl,
-/* Read a key with ID and return it in an allocate buffer pointed to
- by r_BUF as a valid S-expression. */
+struct readkey_status_parm_s
+{
+ char *keyref;
+};
+
+static gpg_error_t
+readkey_status_cb (void *opaque, const char *line)
+{
+ struct readkey_status_parm_s *parm = opaque;
+ gpg_error_t err = 0;
+ char *line_buffer = NULL;
+ const char *s;
+
+ if ((s = has_leading_keyword (line, "KEYPAIRINFO"))
+ && !parm->keyref)
+ {
+ /* The format of such a line is:
+ * KEYPAIRINFO <hexgrip> <keyref> [usage] [keytime]
+ *
+ * Here we only need the keyref. We use only the first received
+ * KEYPAIRINFO; it is possible to receive several if there are
+ * two or more active cards with the same key. */
+ char *fields[2];
+ int nfields;
+
+ line_buffer = xtrystrdup (line);
+ if (!line_buffer)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ if ((nfields = split_fields (line_buffer, fields, DIM (fields))) < 2)
+ goto leave; /* Not enough args; invalid status line - skip. */
+
+ parm->keyref = xtrystrdup (fields[1]);
+ if (!parm->keyref)
+ err = gpg_error_from_syserror ();
+ }
+ else
+ err = pincache_put_cb (NULL, line);
+
+ leave:
+ xfree (line_buffer);
+ return err;
+}
+
+
+/* Read a key with ID (keyref or keygrip) and return it in a malloced
+ * buffer pointed to by R_BUF as a valid S-expression. If R_KEYREF is
+ * not NULL the keyref is stored there. */
int
-agent_card_readkey (ctrl_t ctrl, const char *id, unsigned char **r_buf)
+agent_card_readkey (ctrl_t ctrl, const char *id,
+ unsigned char **r_buf, char **r_keyref)
{
int rc;
char line[ASSUAN_LINELENGTH];
membuf_t data;
size_t len, buflen;
+ struct readkey_status_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
*r_buf = NULL;
+ if (r_keyref)
+ *r_keyref = NULL;
+
rc = start_scd (ctrl);
if (rc)
return rc;
init_membuf (&data, 1024);
- snprintf (line, DIM(line), "READKEY %s", id);
+ snprintf (line, DIM(line), "READKEY%s -- %s",
+ r_keyref? " --info":"", id);
rc = assuan_transact (ctrl->scd_local->ctx, line,
put_membuf_cb, &data,
NULL, NULL,
- pincache_put_cb, NULL);
+ readkey_status_cb, &parm);
if (rc)
{
xfree (get_membuf (&data, &len));
+ xfree (parm.keyref);
return unlock_scd (ctrl, rc);
}
*r_buf = get_membuf (&data, &buflen);
if (!*r_buf)
- return unlock_scd (ctrl, gpg_error (GPG_ERR_ENOMEM));
+ {
+ xfree (parm.keyref);
+ return unlock_scd (ctrl, gpg_error (GPG_ERR_ENOMEM));
+ }
if (!gcry_sexp_canon_len (*r_buf, buflen, NULL, NULL))
{
+ xfree (parm.keyref);
xfree (*r_buf); *r_buf = NULL;
return unlock_scd (ctrl, gpg_error (GPG_ERR_INV_VALUE));
}
+ if (r_keyref)
+ *r_keyref = parm.keyref;
+ else
+ xfree (parm.keyref);
return unlock_scd (ctrl, 0);
}
diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index 243ce76bb..8adbe01cd 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -2394,7 +2394,7 @@ card_key_available (ctrl_t ctrl, const struct card_key_info_s *keyinfo,
*cardsn = NULL;
/* Read the public key. */
- err = agent_card_readkey (ctrl, keyinfo->keygrip, &pkbuf);
+ err = agent_card_readkey (ctrl, keyinfo->keygrip, &pkbuf, NULL);
if (err)
{
if (opt.verbose)
diff --git a/agent/command.c b/agent/command.c
index 5f29adbe6..1b07600c3 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1035,7 +1035,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
goto leave;
}
- rc = agent_card_readkey (ctrl, keyid, &pkbuf);
+ rc = agent_card_readkey (ctrl, keyid, &pkbuf, NULL);
if (rc)
goto leave;
pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
diff --git a/agent/divert-scd.c b/agent/divert-scd.c
index de072e629..d8076d158 100644
--- a/agent/divert-scd.c
+++ b/agent/divert-scd.c
@@ -46,11 +46,16 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info,
*r_kid = NULL;
bin2hex (grip, 20, hexgrip);
- err = parse_shadow_info (shadow_info, &want_sn, NULL, NULL);
- if (err)
- return err;
+ if (shadow_info)
+ {
+ err = parse_shadow_info (shadow_info, &want_sn, NULL, NULL);
+ if (err)
+ return err;
+ }
+ else
+ want_sn = NULL;
- len = strlen (want_sn);
+ len = want_sn? strlen (want_sn) : 0;
if (len == 32 && !strncmp (want_sn, "D27600012401", 12))
{
/* This is an OpenPGP card - reformat */
@@ -90,7 +95,9 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info,
}
}
- if (asprintf (&desc,
+ if (!want_sn)
+ ; /* No shadow info so we can't ask; ERR is already set. */
+ else if (asprintf (&desc,
"%s:%%0A%%0A"
" %s",
L_("Please insert the card with serial number"),
@@ -407,6 +414,9 @@ getpin_cb (void *opaque, const char *desc_text, const char *info,
* smartcard. DESC_TEXT is the original text for a prompt has send by
* gpg to gpg-agent.
*
+ * Note: If SHADOW_INFO is NULL the user can't be asked to insert the
+ * card, we simply try to use an inserted card with the given keygrip.
+ *
* FIXME: Explain the other args. */
int
divert_pksign (ctrl_t ctrl, const char *desc_text, const unsigned char *grip,
@@ -424,6 +434,8 @@ divert_pksign (ctrl_t ctrl, const char *desc_text, const unsigned char *grip,
rc = ask_for_card (ctrl, shadow_info, grip, &kid);
if (rc)
return rc;
+ /* Note that the KID may be an keyref or a keygrip. The signing
+ * functions handle both. */
if (algo == MD_USER_TLS_MD5SHA1)
{
diff --git a/agent/findkey.c b/agent/findkey.c
index 370050d8b..69c90b37f 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -1166,6 +1166,8 @@ key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
if (!list)
list = gcry_sexp_find_token (s_key, "private-key", 0 );
if (!list)
+ list = gcry_sexp_find_token (s_key, "public-key", 0 );
+ if (!list)
{
log_error ("invalid private key format\n");
return gpg_error (GPG_ERR_BAD_SECKEY);
diff --git a/agent/learncard.c b/agent/learncard.c
index f40f5ac4d..678ff9b96 100644
--- a/agent/learncard.c
+++ b/agent/learncard.c
@@ -401,7 +401,7 @@ agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force)
continue; /* The key is already available. */
/* Unknown key - store it. */
- rc = agent_card_readkey (ctrl, item->id, &pubkey);
+ rc = agent_card_readkey (ctrl, item->id, &pubkey, NULL);
if (rc)
{
log_debug ("agent_card_readkey failed: %s\n", gpg_strerror (rc));
diff --git a/agent/pksign.c b/agent/pksign.c
index 4a43b09de..8e88deecc 100644
--- a/agent/pksign.c
+++ b/agent/pksign.c
@@ -294,6 +294,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
gcry_sexp_t s_hash = NULL;
gcry_sexp_t s_pkey = NULL;
unsigned char *shadow_info = NULL;
+ int no_shadow_info = 0;
const unsigned char *data;
int datalen;
int check_signature = 0;
@@ -315,16 +316,19 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
err = agent_key_from_file (ctrl, cache_nonce, desc_text, ctrl->keygrip,
&shadow_info, cache_mode, lookup_ttl,
&s_skey, NULL);
- if (err)
+ if (gpg_err_code (err) == GPG_ERR_NO_SECKEY)
+ no_shadow_info = 1;
+ else if (err)
{
- if (gpg_err_code (err) != GPG_ERR_NO_SECKEY)
- log_error ("failed to read the secret key\n");
+ log_error ("failed to read the secret key\n");
goto leave;
}
- if (shadow_info)
+ if (shadow_info || no_shadow_info)
{
- /* Divert operation to the smartcard */
+ /* Divert operation to the smartcard. With NO_SHADOW_INFO set
+ * we don't have the keystub but we want to see whether the key
+ * is on the active card. */
size_t len;
unsigned char *buf = NULL;
int key_type;
@@ -332,18 +336,65 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
int is_ECDSA = 0;
int is_EdDSA = 0;
- err = agent_public_key_from_file (ctrl, ctrl->keygrip, &s_pkey);
- if (err)
+ if (no_shadow_info)
{
- log_error ("failed to read the public key\n");
- goto leave;
+ /* Try to get the public key from the card or fail with the
+ * original NO_SECKEY error. We also write a stub file (we
+ * are here only because no stub exists). */
+ char *serialno;
+ unsigned char *pkbuf = NULL;
+ size_t pkbuflen;
+ char hexgrip[2*KEYGRIP_LEN+1];
+ char *keyref;
+
+ if (agent_card_serialno (ctrl, &serialno, NULL))
+ {
+ /* No card availabale or error reading the card. */
+ err = gpg_error (GPG_ERR_NO_SECKEY);
+ goto leave;
+ }
+ bin2hex (ctrl->keygrip, KEYGRIP_LEN, hexgrip);
+ if (agent_card_readkey (ctrl, hexgrip, &pkbuf, &keyref))
+ {
+ /* No such key on the card. */
+ xfree (serialno);
+ err = gpg_error (GPG_ERR_NO_SECKEY);
+ goto leave;
+ }
+ pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
+ err = gcry_sexp_sscan (&s_pkey, NULL, (char*)pkbuf, pkbuflen);
+ if (err)
+ {
+ xfree (serialno);
+ xfree (pkbuf);
+ xfree (keyref);
+ log_error ("%s: corrupted key returned by scdaemon\n", __func__);
+ goto leave;
+ }
+
+ if (keyref)
+ agent_write_shadow_key (ctrl->keygrip, serialno, keyref, pkbuf, 0);
+
+ xfree (serialno);
+ xfree (pkbuf);
+ xfree (keyref);
+ }
+ else
+ {
+ /* Get the public key from the stub file. */
+ err = agent_public_key_from_file (ctrl, ctrl->keygrip, &s_pkey);
+ if (err)
+ {
+ log_error ("failed to read the public key\n");
+ goto leave;
+ }
}
- if (agent_is_eddsa_key (s_skey))
+ if (agent_is_eddsa_key (s_pkey))
is_EdDSA = 1;
else
{
- key_type = agent_is_dsa_key (s_skey);
+ key_type = agent_is_dsa_key (s_pkey);
if (key_type == 0)
is_RSA = 1;
else if (key_type == GCRY_PK_ECDSA)
@@ -354,7 +405,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
char *desc2 = NULL;
if (desc_text)
- agent_modify_description (desc_text, NULL, s_skey, &desc2);
+ agent_modify_description (desc_text, NULL, s_pkey, &desc2);
err = divert_pksign (ctrl, desc2? desc2 : desc_text,
ctrl->keygrip,
@@ -444,7 +495,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
}
else
{
- /* No smartcard, but a private key */
+ /* No smartcard, but a private key (in S_SKEY). */
int dsaalgo = 0;
/* Put the hash into a sexp */
@@ -494,7 +545,8 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
/* Check that the signature verification worked and nothing is
* fooling us e.g. by a bug in the signature create code or by
* deliberately introduced faults. Because Libgcrypt 1.7 does this
- * for RSA internally there is no need to do it here again. */
+ * for RSA internally there is no need to do it here again. We do
+ * this always for card based RSA keys, though. */
if (check_signature)
{
gcry_sexp_t sexp_key = s_pkey? s_pkey: s_skey;