diff options
author | Werner Koch <wk@gnupg.org> | 2009-03-06 18:31:27 +0100 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2009-03-06 18:31:27 +0100 |
commit | a9c317a95c440a083809346d61cdb78abff71b12 (patch) | |
tree | 6f5199efe8fba5473afc346f003abe74f6ab424e /agent | |
parent | New PIN Callback attributes in gpg-agent. (diff) | |
download | gnupg2-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/ChangeLog | 15 | ||||
-rw-r--r-- | agent/agent.h | 5 | ||||
-rw-r--r-- | agent/command.c | 161 | ||||
-rw-r--r-- | agent/divert-scd.c | 40 | ||||
-rw-r--r-- | agent/findkey.c | 130 | ||||
-rw-r--r-- | agent/protect.c | 67 |
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; +} + |