summaryrefslogtreecommitdiffstats
path: root/agent
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2009-03-06 18:31:27 +0100
committerWerner Koch <wk@gnupg.org>2009-03-06 18:31:27 +0100
commita9c317a95c440a083809346d61cdb78abff71b12 (patch)
tree6f5199efe8fba5473afc346f003abe74f6ab424e /agent
parentNew PIN Callback attributes in gpg-agent. (diff)
downloadgnupg2-a9c317a95c440a083809346d61cdb78abff71b12.tar.xz
gnupg2-a9c317a95c440a083809346d61cdb78abff71b12.zip
New gpg-agent command to list key information.
Gpgsm does now print the S/N of cards. Consider ephemeral keys during listing an export.
Diffstat (limited to 'agent')
-rw-r--r--agent/ChangeLog15
-rw-r--r--agent/agent.h5
-rw-r--r--agent/command.c161
-rw-r--r--agent/divert-scd.c40
-rw-r--r--agent/findkey.c130
-rw-r--r--agent/protect.c67
6 files changed, 348 insertions, 70 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog
index e016377a1..cae70f3bb 100644
--- a/agent/ChangeLog
+++ b/agent/ChangeLog
@@ -1,3 +1,16 @@
+2009-03-06 Werner Koch <wk@g10code.com>
+
+ * command.c (cmd_keyinfo): New command.
+ (register_commands): Register it.
+ (agent_write_status): Make sure not to print LR or CR.
+ * divert-scd.c (ask_for_card): Factor shadow info parsing out to ...
+ * protect.c (parse_shadow_info): New.
+ * findkey.c (agent_key_from_file): Use make_canon_sexp.
+ (agent_write_private_key, unprotect, read_key_file)
+ (agent_key_available): Use bin2hex.
+ (agent_key_info_from_file): New.
+ (read_key_file): Log no error message for ENOENT.
+
2009-03-05 Werner Koch <wk@g10code.com>
* divert-scd.c (getpin_cb): Support flag 'P'. Change max_digits
@@ -2227,7 +2240,7 @@ Fri Aug 18 14:27:14 CEST 2000 Werner Koch <wk@openit.de>
Copyright 2001, 2002, 2003, 2004, 2005,
- 2007 Free Software Foundation, Inc.
+ 2007, 2008, 2009 Free Software Foundation, Inc.
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without
diff --git a/agent/agent.h b/agent/agent.h
index 0e2cc9f40..fa2c61d06 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -233,6 +233,9 @@ gpg_error_t agent_public_key_from_file (ctrl_t ctrl,
const unsigned char *grip,
gcry_sexp_t *result);
int agent_key_available (const unsigned char *grip);
+gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
+ int *r_keytype,
+ unsigned char **r_shadow_info);
/*-- call-pinentry.c --*/
void initialize_module_call_pinentry (void);
@@ -294,6 +297,8 @@ int agent_shadow_key (const unsigned char *pubkey,
unsigned char **result);
int agent_get_shadow_info (const unsigned char *shadowkey,
unsigned char const **shadow_info);
+gpg_error_t parse_shadow_info (const unsigned char *shadow_info,
+ char **r_hexsn, char **r_idstr);
/*-- trustlist.c --*/
diff --git a/agent/command.c b/agent/command.c
index 9ebcd091f..ba0f8fc4c 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1,6 +1,6 @@
/* command.c - gpg-agent command handler
* Copyright (C) 2001, 2002, 2003, 2004, 2005,
- * 2006, 2008 Free Software Foundation, Inc.
+ * 2006, 2008, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -30,6 +30,9 @@
#include <ctype.h>
#include <unistd.h>
#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
#include <assuan.h>
@@ -308,8 +311,21 @@ agent_write_status (ctrl_t ctrl, const char *keyword, ...)
*p++ = ' ';
n++;
}
- for ( ; *text && n < DIM (buf)-2; n++)
- *p++ = *text++;
+ for ( ; *text && n < DIM (buf)-3; n++, text++)
+ {
+ if (*text == '\n')
+ {
+ *p++ = '\\';
+ *p++ = 'n';
+ }
+ else if (*text == '\r')
+ {
+ *p++ = '\\';
+ *p++ = 'r';
+ }
+ else
+ *p++ = *text;
+ }
}
*p = 0;
err = assuan_write_status (ctx, keyword, buf);
@@ -806,7 +822,145 @@ cmd_readkey (assuan_context_t ctx, char *line)
}
+
+/* KEYINFO [--list] <keygrip>
+
+ Return information about the key specified by the KEYGRIP. If the
+ key is not available GPG_ERR_NOT_FOUND is returned. If the option
+ --list is given the keygrip is ignored and information about all
+ available keys are returned. The information is returned as a
+ status line with this format:
+
+ KEYINFO <keygrip> <type> <serialno> <idstr>
+
+ KEYGRIP is the keygrip.
+
+ TYPE is describes the type of the key:
+ 'D' - Regular key stored on disk,
+ 'T' - Key is stored on a smartcard (token).
+ '-' - Unknown type.
+
+ SERIALNO is an ASCII string with the serial number of the
+ smartcard. If the serial number is not known a single
+ dash '-' is used instead.
+
+ IDSTR is the IDSTR used to distinguish keys on a smartcard. If it
+ is not known a dash is used instead.
+
+ More information may be added in the future.
+*/
+static gpg_error_t
+do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip)
+{
+ gpg_error_t err;
+ char hexgrip[40+1];
+ int keytype;
+ unsigned char *shadow_info = NULL;
+ char *serialno = NULL;
+ char *idstr = NULL;
+ const char *keytypestr;
+
+ err = agent_key_info_from_file (ctrl, grip, &keytype, &shadow_info);
+ if (err)
+ goto leave;
+
+ /* Reformat the grip so that we use uppercase as good style. */
+ bin2hex (grip, 20, hexgrip);
+
+ if (keytype == PRIVATE_KEY_CLEAR
+ || keytype == PRIVATE_KEY_PROTECTED)
+ keytypestr = "D";
+ else if (keytype == PRIVATE_KEY_SHADOWED)
+ keytypestr = "T";
+ else
+ keytypestr = "-";
+
+ if (shadow_info)
+ {
+ err = parse_shadow_info (shadow_info, &serialno, &idstr);
+ if (err)
+ goto leave;
+ }
+
+ err = agent_write_status (ctrl, "KEYINFO",
+ hexgrip,
+ keytypestr,
+ serialno? serialno : "-",
+ idstr? idstr : "-",
+ NULL);
+ leave:
+ xfree (shadow_info);
+ xfree (serialno);
+ xfree (idstr);
+ return err;
+}
+
+
+static int
+cmd_keyinfo (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int err;
+ unsigned char grip[20];
+ DIR *dir = NULL;
+ int list_mode;
+
+ list_mode = has_option (line, "--list");
+ line = skip_options (line);
+
+ if (list_mode)
+ {
+ char *dirname;
+ struct dirent *dir_entry;
+ char hexgrip[41];
+
+ dirname = make_filename_try (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, NULL);
+ if (!dirname)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ dir = opendir (dirname);
+ if (!dir)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (dirname);
+ goto leave;
+ }
+ xfree (dirname);
+ while ( (dir_entry = readdir (dir)) )
+ {
+ if (strlen (dir_entry->d_name) != 44
+ || strcmp (dir_entry->d_name + 40, ".key"))
+ continue;
+ strncpy (hexgrip, dir_entry->d_name, 40);
+ hexgrip[40] = 0;
+
+ if ( hex2bin (hexgrip, grip, 20) < 0 )
+ continue; /* Bad hex string. */
+
+ err = do_one_keyinfo (ctrl, grip);
+ if (err)
+ goto leave;
+ }
+ err = 0;
+ }
+ else
+ {
+ err = parse_keygrip (ctx, line, grip);
+ if (err)
+ goto leave;
+ err = do_one_keyinfo (ctrl, grip);
+ }
+
+ leave:
+ if (dir)
+ closedir (dir);
+ if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+ log_error ("command keyinfo failed: %s\n", gpg_strerror (err));
+ return err;
+}
@@ -1574,6 +1728,7 @@ register_commands (assuan_context_t ctx)
{ "GETEVENTCOUNTER",cmd_geteventcounter },
{ "ISTRUSTED", cmd_istrusted },
{ "HAVEKEY", cmd_havekey },
+ { "KEYINFO", cmd_keyinfo },
{ "SIGKEY", cmd_sigkey },
{ "SETKEY", cmd_sigkey },
{ "SETKEYDESC", cmd_setkeydesc },
diff --git a/agent/divert-scd.c b/agent/divert-scd.c
index a583f1a61..fd8c28b66 100644
--- a/agent/divert-scd.c
+++ b/agent/divert-scd.c
@@ -28,16 +28,14 @@
#include <sys/stat.h>
#include "agent.h"
-#include "sexp-parse.h"
#include "i18n.h"
+#include "sexp-parse.h"
static int
ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
{
int rc, i;
- const unsigned char *s;
- size_t n;
char *serialno;
int no_card = 0;
char *desc;
@@ -45,39 +43,19 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
int want_sn_displen;
*r_kid = NULL;
- s = shadow_info;
- if (*s != '(')
- return gpg_error (GPG_ERR_INV_SEXP);
- s++;
- n = snext (&s);
- if (!n)
- return gpg_error (GPG_ERR_INV_SEXP);
- want_sn = xtrymalloc (n*2+1);
- if (!want_sn)
- return out_of_core ();
- for (i=0; i < n; i++)
- sprintf (want_sn+2*i, "%02X", s[i]);
- s += n;
+
+ rc = parse_shadow_info (shadow_info, &want_sn, &want_kid);
+ if (rc)
+ return rc;
+
/* We assume that a 20 byte serial number is a standard one which
- seems to have the property to have a zero in the last nibble. We
- don't display this '0' because it may confuse the user */
+ has the property to have a zero in the last nibble (Due to BCD
+ representation). We don't display this '0' because it may
+ confuse the user. */
want_sn_displen = strlen (want_sn);
if (want_sn_displen == 20 && want_sn[19] == '0')
want_sn_displen--;
- n = snext (&s);
- if (!n)
- return gpg_error (GPG_ERR_INV_SEXP);
- want_kid = xtrymalloc (n+1);
- if (!want_kid)
- {
- gpg_error_t tmperr = out_of_core ();
- xfree (want_sn);
- return tmperr;
- }
- memcpy (want_kid, s, n);
- want_kid[n] = 0;
-
for (;;)
{
rc = agent_card_serialno (ctrl, &serialno);
diff --git a/agent/findkey.c b/agent/findkey.c
index 0bb6afdcb..5bea198dc 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -56,14 +56,12 @@ int
agent_write_private_key (const unsigned char *grip,
const void *buffer, size_t length, int force)
{
- int i;
char *fname;
FILE *fp;
char hexgrip[40+4+1];
int fd;
- for (i=0; i < 20; i++)
- sprintf (hexgrip+2*i, "%02X", grip[i]);
+ bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key");
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
@@ -307,14 +305,12 @@ unprotect (ctrl_t ctrl, const char *desc_text,
{
struct pin_entry_info_s *pi;
struct try_unprotect_arg_s arg;
- int rc, i;
+ int rc;
unsigned char *result;
size_t resultlen;
char hexgrip[40+1];
- for (i=0; i < 20; i++)
- sprintf (hexgrip+2*i, "%02X", grip[i]);
- hexgrip[40] = 0;
+ bin2hex (grip, 20, hexgrip);
/* First try to get it from the cache - if there is none or we can't
unprotect it, we fall back to ask the user */
@@ -425,7 +421,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
static gpg_error_t
read_key_file (const unsigned char *grip, gcry_sexp_t *result)
{
- int i, rc;
+ int rc;
char *fname;
FILE *fp;
struct stat st;
@@ -436,8 +432,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
*result = NULL;
- for (i=0; i < 20; i++)
- sprintf (hexgrip+2*i, "%02X", grip[i]);
+ bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key");
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
@@ -445,7 +440,8 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
if (!fp)
{
rc = gpg_error_from_syserror ();
- log_error ("can't open `%s': %s\n", fname, strerror (errno));
+ if (gpg_err_code (rc) != GPG_ERR_ENOENT)
+ log_error ("can't open `%s': %s\n", fname, strerror (errno));
xfree (fname);
return rc;
}
@@ -488,11 +484,11 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
/* Return the secret key as an S-Exp in RESULT after locating it using
- the grip. Returns NULL in RESULT if the operation should be
- diverted to a token; SHADOW_INFO will point then to an allocated
- S-Expression with the shadow_info part from the file. CACHE_MODE
- defines now the cache shall be used. DESC_TEXT may be set to
- present a custom description for the pinentry. */
+ the GRIP. Stores NULL at RESULT if the operation shall be diverted
+ to a token; in this case an allocated S-expression with the
+ shadow_info part from the file is stored at SHADOW_INFO.
+ CACHE_MODE defines now the cache shall be used. DESC_TEXT may be
+ set to present a custom description for the pinentry. */
gpg_error_t
agent_key_from_file (ctrl_t ctrl, const char *desc_text,
const unsigned char *grip, unsigned char **shadow_info,
@@ -513,20 +509,11 @@ agent_key_from_file (ctrl_t ctrl, const char *desc_text,
return rc;
/* For use with the protection functions we also need the key as an
- canonical encoded S-expression in abuffer. Create this buffer
+ canonical encoded S-expression in a buffer. Create this buffer
now. */
- len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0);
- assert (len);
- buf = xtrymalloc (len);
- if (!buf)
- {
- rc = gpg_error_from_syserror ();
- gcry_sexp_release (s_skey);
- return rc;
- }
- len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, buf, len);
- assert (len);
-
+ rc = make_canon_sexp (s_skey, &buf, &len);
+ if (rc)
+ return rc;
switch (agent_private_key_type (buf))
{
@@ -842,19 +829,94 @@ agent_public_key_from_file (ctrl_t ctrl,
int
agent_key_available (const unsigned char *grip)
{
- int i;
+ int result;
char *fname;
char hexgrip[40+4+1];
- for (i=0; i < 20; i++)
- sprintf (hexgrip+2*i, "%02X", grip[i]);
+ bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key");
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
- i = !access (fname, R_OK)? 0 : -1;
+ result = !access (fname, R_OK)? 0 : -1;
xfree (fname);
- return i;
+ return result;
}
+/* Return the information about the secret key specified by the binary
+ keygrip GRIP. If the key is a shadowed one the shadow information
+ will be stored at the address R_SHADOW_INFO as an allocated
+ S-expression. */
+gpg_error_t
+agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
+ int *r_keytype, unsigned char **r_shadow_info)
+{
+ gpg_error_t err;
+ unsigned char *buf;
+ size_t len;
+ int keytype;
+
+ (void)ctrl;
+
+ if (r_keytype)
+ *r_keytype = PRIVATE_KEY_UNKNOWN;
+ if (r_shadow_info)
+ *r_shadow_info = NULL;
+
+ {
+ gcry_sexp_t sexp;
+
+ err = read_key_file (grip, &sexp);
+ if (err)
+ {
+ if (gpg_err_code (err) == GPG_ERR_ENOENT)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ else
+ return err;
+ }
+ err = make_canon_sexp (sexp, &buf, &len);
+ gcry_sexp_release (sexp);
+ if (err)
+ return err;
+ }
+
+ keytype = agent_private_key_type (buf);
+ switch (keytype)
+ {
+ case PRIVATE_KEY_CLEAR:
+ break;
+ case PRIVATE_KEY_PROTECTED:
+ /* If we ever require it we could retrieve the comment fields
+ from such a key. */
+ break;
+ case PRIVATE_KEY_SHADOWED:
+ if (r_shadow_info)
+ {
+ const unsigned char *s;
+ size_t n;
+
+ err = agent_get_shadow_info (buf, &s);
+ if (!err)
+ {
+ n = gcry_sexp_canon_len (s, 0, NULL, NULL);
+ assert (n);
+ *r_shadow_info = xtrymalloc (n);
+ if (!*r_shadow_info)
+ err = gpg_error_from_syserror ();
+ else
+ memcpy (*r_shadow_info, s, n);
+ }
+ }
+ break;
+ default:
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ break;
+ }
+
+ if (!err && r_keytype)
+ *r_keytype = keytype;
+
+ xfree (buf);
+ return err;
+}
diff --git a/agent/protect.c b/agent/protect.c
index ebb02ac89..8b022ecfb 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -1,6 +1,6 @@
/* protect.c - Un/Protect a secret key
* Copyright (C) 1998, 1999, 2000, 2001, 2002,
- * 2003, 2007 Free Software Foundation, Inc.
+ * 2003, 2007, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -1105,3 +1105,68 @@ agent_get_shadow_info (const unsigned char *shadowkey,
return 0;
}
+
+/* Parse the canonical encoded SHADOW_INFO S-expression. On success
+ the hex encoded serial number is returned as a malloced strings at
+ R_HEXSN and the Id string as a malloced string at R_IDSTR. On
+ error an error code is returned and NULL is stored at the result
+ parameters addresses. If the serial number or the ID string is not
+ required, NULL may be passed for them. */
+gpg_error_t
+parse_shadow_info (const unsigned char *shadow_info,
+ char **r_hexsn, char **r_idstr)
+{
+ const unsigned char *s;
+ size_t n;
+
+ if (r_hexsn)
+ *r_hexsn = NULL;
+ if (r_idstr)
+ *r_idstr = NULL;
+
+ s = shadow_info;
+ if (*s != '(')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ s++;
+ n = snext (&s);
+ if (!n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+
+ if (r_hexsn)
+ {
+ *r_hexsn = bin2hex (s, n, NULL);
+ if (!*r_hexsn)
+ return gpg_error_from_syserror ();
+ }
+ s += n;
+
+ n = snext (&s);
+ if (!n)
+ {
+ if (r_hexsn)
+ {
+ xfree (*r_hexsn);
+ *r_hexsn = NULL;
+ }
+ return gpg_error (GPG_ERR_INV_SEXP);
+ }
+
+ if (r_idstr)
+ {
+ *r_idstr = xtrymalloc (n+1);
+ if (!*r_idstr)
+ {
+ if (r_hexsn)
+ {
+ xfree (*r_hexsn);
+ *r_hexsn = NULL;
+ }
+ return gpg_error_from_syserror ();
+ }
+ memcpy (*r_idstr, s, n);
+ (*r_idstr)[n] = 0;
+ }
+
+ return 0;
+}
+