New gpg-agent command to list key information.

Gpgsm does now print the S/N of cards.
Consider ephemeral keys during listing an export.
This commit is contained in:
Werner Koch 2009-03-06 17:31:27 +00:00
parent 59d7a54e72
commit a9c317a95c
20 changed files with 601 additions and 180 deletions

6
NEWS
View File

@ -1,6 +1,12 @@
Noteworthy changes in version 2.0.12 Noteworthy changes in version 2.0.12
------------------------------------------------- -------------------------------------------------
* GPGSM now always lists ephemeral certificates if specified by
fingerprint or keygrip.
* New command "KEYINFO" for GPG_AGENT. GPGSM now also returns
information about smartcards.
Noteworthy changes in version 2.0.11 (2009-03-03) Noteworthy changes in version 2.0.11 (2009-03-03)
------------------------------------------------- -------------------------------------------------

View File

@ -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> 2009-03-05 Werner Koch <wk@g10code.com>
* divert-scd.c (getpin_cb): Support flag 'P'. Change max_digits * 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, 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 This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without unlimited permission to copy and/or distribute it, with or without

View File

@ -233,6 +233,9 @@ gpg_error_t agent_public_key_from_file (ctrl_t ctrl,
const unsigned char *grip, const unsigned char *grip,
gcry_sexp_t *result); gcry_sexp_t *result);
int agent_key_available (const unsigned char *grip); 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 --*/ /*-- call-pinentry.c --*/
void initialize_module_call_pinentry (void); void initialize_module_call_pinentry (void);
@ -294,6 +297,8 @@ int agent_shadow_key (const unsigned char *pubkey,
unsigned char **result); unsigned char **result);
int agent_get_shadow_info (const unsigned char *shadowkey, int agent_get_shadow_info (const unsigned char *shadowkey,
unsigned char const **shadow_info); unsigned char const **shadow_info);
gpg_error_t parse_shadow_info (const unsigned char *shadow_info,
char **r_hexsn, char **r_idstr);
/*-- trustlist.c --*/ /*-- trustlist.c --*/

View File

@ -1,6 +1,6 @@
/* command.c - gpg-agent command handler /* command.c - gpg-agent command handler
* Copyright (C) 2001, 2002, 2003, 2004, 2005, * 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. * This file is part of GnuPG.
* *
@ -30,6 +30,9 @@
#include <ctype.h> #include <ctype.h>
#include <unistd.h> #include <unistd.h>
#include <assert.h> #include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <assuan.h> #include <assuan.h>
@ -308,8 +311,21 @@ agent_write_status (ctrl_t ctrl, const char *keyword, ...)
*p++ = ' '; *p++ = ' ';
n++; n++;
} }
for ( ; *text && n < DIM (buf)-2; n++) for ( ; *text && n < DIM (buf)-3; n++, text++)
*p++ = *text++; {
if (*text == '\n')
{
*p++ = '\\';
*p++ = 'n';
}
else if (*text == '\r')
{
*p++ = '\\';
*p++ = 'r';
}
else
*p++ = *text;
}
} }
*p = 0; *p = 0;
err = assuan_write_status (ctx, keyword, buf); 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 }, { "GETEVENTCOUNTER",cmd_geteventcounter },
{ "ISTRUSTED", cmd_istrusted }, { "ISTRUSTED", cmd_istrusted },
{ "HAVEKEY", cmd_havekey }, { "HAVEKEY", cmd_havekey },
{ "KEYINFO", cmd_keyinfo },
{ "SIGKEY", cmd_sigkey }, { "SIGKEY", cmd_sigkey },
{ "SETKEY", cmd_sigkey }, { "SETKEY", cmd_sigkey },
{ "SETKEYDESC", cmd_setkeydesc }, { "SETKEYDESC", cmd_setkeydesc },

View File

@ -28,16 +28,14 @@
#include <sys/stat.h> #include <sys/stat.h>
#include "agent.h" #include "agent.h"
#include "sexp-parse.h"
#include "i18n.h" #include "i18n.h"
#include "sexp-parse.h"
static int static int
ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
{ {
int rc, i; int rc, i;
const unsigned char *s;
size_t n;
char *serialno; char *serialno;
int no_card = 0; int no_card = 0;
char *desc; char *desc;
@ -45,39 +43,19 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
int want_sn_displen; int want_sn_displen;
*r_kid = NULL; *r_kid = NULL;
s = shadow_info;
if (*s != '(') rc = parse_shadow_info (shadow_info, &want_sn, &want_kid);
return gpg_error (GPG_ERR_INV_SEXP); if (rc)
s++; return rc;
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;
/* We assume that a 20 byte serial number is a standard one which /* 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 has the property to have a zero in the last nibble (Due to BCD
don't display this '0' because it may confuse the user */ representation). We don't display this '0' because it may
confuse the user. */
want_sn_displen = strlen (want_sn); want_sn_displen = strlen (want_sn);
if (want_sn_displen == 20 && want_sn[19] == '0') if (want_sn_displen == 20 && want_sn[19] == '0')
want_sn_displen--; 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 (;;) for (;;)
{ {
rc = agent_card_serialno (ctrl, &serialno); rc = agent_card_serialno (ctrl, &serialno);

View File

@ -56,14 +56,12 @@ int
agent_write_private_key (const unsigned char *grip, agent_write_private_key (const unsigned char *grip,
const void *buffer, size_t length, int force) const void *buffer, size_t length, int force)
{ {
int i;
char *fname; char *fname;
FILE *fp; FILE *fp;
char hexgrip[40+4+1]; char hexgrip[40+4+1];
int fd; int fd;
for (i=0; i < 20; i++) bin2hex (grip, 20, hexgrip);
sprintf (hexgrip+2*i, "%02X", grip[i]);
strcpy (hexgrip+40, ".key"); strcpy (hexgrip+40, ".key");
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); 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 pin_entry_info_s *pi;
struct try_unprotect_arg_s arg; struct try_unprotect_arg_s arg;
int rc, i; int rc;
unsigned char *result; unsigned char *result;
size_t resultlen; size_t resultlen;
char hexgrip[40+1]; char hexgrip[40+1];
for (i=0; i < 20; i++) bin2hex (grip, 20, hexgrip);
sprintf (hexgrip+2*i, "%02X", grip[i]);
hexgrip[40] = 0;
/* First try to get it from the cache - if there is none or we can't /* 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 */ 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 static gpg_error_t
read_key_file (const unsigned char *grip, gcry_sexp_t *result) read_key_file (const unsigned char *grip, gcry_sexp_t *result)
{ {
int i, rc; int rc;
char *fname; char *fname;
FILE *fp; FILE *fp;
struct stat st; struct stat st;
@ -436,8 +432,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
*result = NULL; *result = NULL;
for (i=0; i < 20; i++) bin2hex (grip, 20, hexgrip);
sprintf (hexgrip+2*i, "%02X", grip[i]);
strcpy (hexgrip+40, ".key"); strcpy (hexgrip+40, ".key");
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); 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) if (!fp)
{ {
rc = gpg_error_from_syserror (); 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); xfree (fname);
return rc; 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 /* 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 the GRIP. Stores NULL at RESULT if the operation shall be diverted
diverted to a token; SHADOW_INFO will point then to an allocated to a token; in this case an allocated S-expression with the
S-Expression with the shadow_info part from the file. CACHE_MODE shadow_info part from the file is stored at SHADOW_INFO.
defines now the cache shall be used. DESC_TEXT may be set to CACHE_MODE defines now the cache shall be used. DESC_TEXT may be
present a custom description for the pinentry. */ set to present a custom description for the pinentry. */
gpg_error_t gpg_error_t
agent_key_from_file (ctrl_t ctrl, const char *desc_text, agent_key_from_file (ctrl_t ctrl, const char *desc_text,
const unsigned char *grip, unsigned char **shadow_info, 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; return rc;
/* For use with the protection functions we also need the key as an /* 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. */ now. */
len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0); rc = make_canon_sexp (s_skey, &buf, &len);
assert (len); if (rc)
buf = xtrymalloc (len); return rc;
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);
switch (agent_private_key_type (buf)) switch (agent_private_key_type (buf))
{ {
@ -842,19 +829,94 @@ agent_public_key_from_file (ctrl_t ctrl,
int int
agent_key_available (const unsigned char *grip) agent_key_available (const unsigned char *grip)
{ {
int i; int result;
char *fname; char *fname;
char hexgrip[40+4+1]; char hexgrip[40+4+1];
for (i=0; i < 20; i++) bin2hex (grip, 20, hexgrip);
sprintf (hexgrip+2*i, "%02X", grip[i]);
strcpy (hexgrip+40, ".key"); strcpy (hexgrip+40, ".key");
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); 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); 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;
}

View File

@ -1,6 +1,6 @@
/* protect.c - Un/Protect a secret key /* protect.c - Un/Protect a secret key
* Copyright (C) 1998, 1999, 2000, 2001, 2002, * 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. * This file is part of GnuPG.
* *
@ -1105,3 +1105,68 @@ agent_get_shadow_info (const unsigned char *shadowkey,
return 0; 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;
}

View File

@ -1,3 +1,7 @@
2009-03-06 Werner Koch <wk@g10code.com>
* sexputil.c (make_canon_sexp): New.
2009-03-03 Werner Koch <wk@g10code.com> 2009-03-03 Werner Koch <wk@g10code.com>
* exechelp.c (do_exec): Make sure that /dev/null connected FDs are * exechelp.c (do_exec): Make sure that /dev/null connected FDs are

View File

@ -1,5 +1,5 @@
/* sexputil.c - Utility functions for S-expressions. /* sexputil.c - Utility functions for S-expressions.
* Copyright (C) 2005, 2007 Free Software Foundation, Inc. * Copyright (C) 2005, 2007, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -34,6 +34,42 @@
#include "util.h" #include "util.h"
#include "sexp-parse.h" #include "sexp-parse.h"
/* Helper function to create a a canonical encoded S-expression from a
Libgcrypt S-expression object. The function returns 0 on success
and the malloced canonical S-expression is stored at R_BUFFER and
the allocated length at R_BUFLEN. On error an error code is
returned and (NULL, 0) stored at R_BUFFER and R_BUFLEN. If the
allocated buffer length is not required, NULL by be used for
R_BUFLEN. */
gpg_error_t
make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen)
{
size_t len;
unsigned char *buf;
*r_buffer = NULL;
if (r_buflen)
*r_buflen = 0;;
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0);
if (!len)
return gpg_error (GPG_ERR_BUG);
buf = xtrymalloc (len);
if (!buf)
return gpg_error_from_syserror ();
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len);
if (!len)
return gpg_error (GPG_ERR_BUG);
*r_buffer = buf;
if (r_buflen)
*r_buflen = len;
return 0;
}
/* Return the so called "keygrip" which is the SHA-1 hash of the /* Return the so called "keygrip" which is the SHA-1 hash of the
public key parameters expressed in a way depended on the algorithm. public key parameters expressed in a way depended on the algorithm.

View File

@ -1,5 +1,5 @@
/* util.h - Utility functions for GnuPG /* util.h - Utility functions for GnuPG
* Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. * Copyright (C) 2001, 2002, 2003, 2004, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -183,6 +183,8 @@ gpg_error_t b64dec_finish (struct b64state *state);
/*-- sexputil.c */ /*-- sexputil.c */
gpg_error_t make_canon_sexp (gcry_sexp_t sexp,
unsigned char **r_buffer, size_t *r_buflen);
gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen, gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen,
unsigned char *grip); unsigned char *grip);
int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b); int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b);

View File

@ -240,10 +240,12 @@ optional @var{pattern}. Those pattern consist of a list of user ids
(@pxref{how-to-specify-a-user-id}). When used along with the (@pxref{how-to-specify-a-user-id}). When used along with the
@option{--armor} option a few informational lines are prepended before @option{--armor} option a few informational lines are prepended before
each block. There is one limitation: As there is no commonly agreed each block. There is one limitation: As there is no commonly agreed
upon way to pack more than one certificate into an ASN.1 structure, the upon way to pack more than one certificate into an ASN.1 structure,
binary export (i.e. without using @option{armor}) works only for the the binary export (i.e. without using @option{armor}) works only for
export of one certificate. Thus it is required to specify a the export of one certificate. Thus it is required to specify a
@var{pattern} which yields exactly one certificate. @var{pattern} which yields exactly one certificate. Ephemeral
certificate are only exported if all @var{pattern} are given as
fingerprints or keygrips.
@item --export-secret-key-p12 @var{key-id} @item --export-secret-key-p12 @var{key-id}
@opindex export @opindex export
@ -601,7 +603,9 @@ forth to @var{epoch} which is the number of seconds elapsed since the year
@item --with-ephemeral-keys @item --with-ephemeral-keys
@opindex with-ephemeral-keys @opindex with-ephemeral-keys
Include ephemeral flagged keys in the output of key listings. Include ephemeral flagged keys in the output of key listings. Note
that they are included anyway if the key specification for a listing
is given as fingerprint or keygrip.
@item --debug-level @var{level} @item --debug-level @var{level}
@opindex debug-level @opindex debug-level

View File

@ -457,7 +457,7 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr)
#ifdef KEYBOX_WITH_X509 #ifdef KEYBOX_WITH_X509
/* Return true if the key in BLOB matches the 20 bytes keygrip GRIP. /* Return true if the key in BLOB matches the 20 bytes keygrip GRIP.
We don't have the keygrips as meta data, thus wen need to parse the We don't have the keygrips as meta data, thus we need to parse the
certificate. Fixme: We might want to return proper error codes certificate. Fixme: We might want to return proper error codes
instead of failing a search for invalid certificates etc. */ instead of failing a search for invalid certificates etc. */
static int static int

View File

@ -1,3 +1,10 @@
2009-03-06 Werner Koch <wk@g10code.com>
* app-nks.c (do_learn_status): Factor code out to..
(do_learn_status_core): .. new.
(do_readcert, do_sign, do_decipher): Switch to SigG if needed.
(verify_pin): Use DESC also for keypad based verify.
2009-03-05 Werner Koch <wk@g10code.com> 2009-03-05 Werner Koch <wk@g10code.com>
* app-openpgp.c (verify_a_chv): Remove special case for keypads. * app-openpgp.c (verify_a_chv): Remove special case for keypads.

View File

@ -308,17 +308,20 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
static void
static gpg_error_t do_learn_status_core (app_t app, ctrl_t ctrl, int is_sigg)
do_learn_status (app_t app, ctrl_t ctrl)
{ {
gpg_error_t err; gpg_error_t err;
char ct_buf[100], id_buf[100]; char ct_buf[100], id_buf[100];
int i; int i;
const char *tag;
err = switch_application (app, 0); if (is_sigg)
if (err) tag = "SIGG";
return err; else if (app->app_local->nks_version < 3)
tag = "DF01";
else
tag = "NKS3";
/* Output information about all useful objects in the NKS application. */ /* Output information about all useful objects in the NKS application. */
for (i=0; filelist[i].fid; i++) for (i=0; filelist[i].fid; i++)
@ -326,7 +329,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
if (filelist[i].nks_ver > app->app_local->nks_version) if (filelist[i].nks_ver > app->app_local->nks_version)
continue; continue;
if (filelist[i].is_sigg) if (!!filelist[i].is_sigg != !!is_sigg)
continue; continue;
if (filelist[i].certtype) if (filelist[i].certtype)
@ -342,8 +345,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
read that many bytes. */ read that many bytes. */
snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype); snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype);
snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X", snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
app->app_local->nks_version < 3? "DF01":"NKS3", tag, filelist[i].fid);
filelist[i].fid);
send_status_info (ctrl, "CERTINFO", send_status_info (ctrl, "CERTINFO",
ct_buf, strlen (ct_buf), ct_buf, strlen (ct_buf),
id_buf, strlen (id_buf), id_buf, strlen (id_buf),
@ -361,8 +363,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
else else
{ {
snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X", snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
app->app_local->nks_version < 3? "DF01":"NKS3", tag, filelist[i].fid);
filelist[i].fid);
send_status_info (ctrl, "KEYPAIRINFO", send_status_info (ctrl, "KEYPAIRINFO",
gripstr, 40, gripstr, 40,
id_buf, strlen (id_buf), id_buf, strlen (id_buf),
@ -371,58 +372,26 @@ do_learn_status (app_t app, ctrl_t ctrl)
} }
} }
}
static gpg_error_t
do_learn_status (app_t app, ctrl_t ctrl)
{
gpg_error_t err;
err = switch_application (app, 0);
if (err)
return err;
do_learn_status_core (app, ctrl, 0);
err = switch_application (app, 1); err = switch_application (app, 1);
if (err) if (err)
return 0; /* Silently ignore if we can't swicth to SigG. */ return 0; /* Silently ignore if we can't switch to SigG. */
for (i=0; filelist[i].fid; i++)
{
if (filelist[i].nks_ver > app->app_local->nks_version)
continue;
if (!filelist[i].is_sigg)
continue;
if (filelist[i].certtype)
{
size_t len;
len = app_help_read_length_of_cert (app->slot,
filelist[i].fid, NULL);
if (len)
{
/* FIXME: We should store the length in the application's
context so that a following readcert does only need to
read that many bytes. */
snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype);
snprintf (id_buf, sizeof id_buf, "NKS-SIGG.%04X",
filelist[i].fid);
send_status_info (ctrl, "CERTINFO",
ct_buf, strlen (ct_buf),
id_buf, strlen (id_buf),
NULL, (size_t)0);
}
}
else if (filelist[i].iskeypair)
{
char gripstr[40+1];
err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr);
if (err)
log_error ("can't get keygrip from FID 0x%04X: %s\n",
filelist[i].fid, gpg_strerror (err));
else
{
snprintf (id_buf, sizeof id_buf, "NKS-SIGG.%04X",
filelist[i].fid);
send_status_info (ctrl, "KEYPAIRINFO",
gripstr, 40,
id_buf, strlen (id_buf),
NULL, (size_t)0);
}
}
}
do_learn_status_core (app, ctrl, 1);
return 0; return 0;
} }
@ -446,20 +415,24 @@ do_readcert (app_t app, const char *certid,
int class, tag, constructed, ndef; int class, tag, constructed, ndef;
size_t totobjlen, objlen, hdrlen; size_t totobjlen, objlen, hdrlen;
int rootca = 0; int rootca = 0;
int is_sigg = 0;
*cert = NULL; *cert = NULL;
*certlen = 0; *certlen = 0;
err = switch_application (app, 0);
if (err)
return err;
if (!strncmp (certid, "NKS-NKS3.", 9)) if (!strncmp (certid, "NKS-NKS3.", 9))
; ;
else if (!strncmp (certid, "NKS-DF01.", 9)) else if (!strncmp (certid, "NKS-DF01.", 9))
; ;
else if (!strncmp (certid, "NKS-SIGG.", 9))
is_sigg = 1;
else else
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
err = switch_application (app, is_sigg);
if (err)
return err;
certid += 9; certid += 9;
if (!hexdigitp (certid) || !hexdigitp (certid+1) if (!hexdigitp (certid) || !hexdigitp (certid+1)
|| !hexdigitp (certid+2) || !hexdigitp (certid+3) || !hexdigitp (certid+2) || !hexdigitp (certid+3)
@ -603,9 +576,7 @@ verify_pin (app_t app, int pwid, const char *desc,
if (!opt.disable_keypad if (!opt.disable_keypad
&& !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) ) && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
{ {
rc = pincb (pincb_arg, rc = pincb (pincb_arg, desc, NULL);
_("||Please enter your PIN at the reader's keypad"),
NULL);
if (rc) if (rc)
{ {
log_info (_("PIN callback returned error: %s\n"), log_info (_("PIN callback returned error: %s\n"),
@ -613,11 +584,8 @@ verify_pin (app_t app, int pwid, const char *desc,
return rc; return rc;
} }
/* Although it is possible to use a local PIN, we use the global rc = iso7816_verify_kp (app->slot, pwid, "", 0, &pininfo);
PIN for this application. */ pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
rc = iso7816_verify_kp (app->slot, 0, "", 0, &pininfo);
/* Dismiss the prompt. */
pincb (pincb_arg, NULL, NULL);
} }
else else
{ {
@ -630,8 +598,6 @@ verify_pin (app_t app, int pwid, const char *desc,
return rc; return rc;
} }
/* The following limits are due to TCOS but also defined in the
NKS specs. */
rc = basic_pin_checks (pinvalue, pininfo.minlen, pininfo.maxlen); rc = basic_pin_checks (pinvalue, pininfo.minlen, pininfo.maxlen);
if (rc) if (rc)
{ {
@ -675,6 +641,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
int rc, i; int rc, i;
int is_sigg = 0;
int fid; int fid;
unsigned char data[35]; /* Must be large enough for a SHA-1 digest unsigned char data[35]; /* Must be large enough for a SHA-1 digest
+ the largest OID _prefix above. */ + the largest OID _prefix above. */
@ -684,19 +651,22 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
if (indatalen != 20 && indatalen != 16 && indatalen != 35) if (indatalen != 20 && indatalen != 16 && indatalen != 35)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
rc = switch_application (app, 0);
if (rc)
return rc;
/* Check that the provided ID is valid. This is not really needed /* Check that the provided ID is valid. This is not really needed
but we do it to enforce correct usage by the caller. */ but we do it to enforce correct usage by the caller. */
if (!strncmp (keyidstr, "NKS-NKS3.", 9) ) if (!strncmp (keyidstr, "NKS-NKS3.", 9) )
; ;
else if (!strncmp (keyidstr, "NKS-DF01.", 9) ) else if (!strncmp (keyidstr, "NKS-DF01.", 9) )
; ;
else if (!strncmp (keyidstr, "NKS-SIGG.", 9) )
is_sigg = 1;
else else
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
keyidstr += 9; keyidstr += 9;
rc = switch_application (app, is_sigg);
if (rc)
return rc;
if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
|| !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
|| keyidstr[4]) || keyidstr[4])
@ -743,7 +713,6 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
/* Decrypt the data in INDATA and return the allocated result in OUTDATA. /* Decrypt the data in INDATA and return the allocated result in OUTDATA.
If a PIN is required the PINCB will be used to ask for the PIN; it If a PIN is required the PINCB will be used to ask for the PIN; it
should return the PIN in an allocated buffer and put it into PIN. */ should return the PIN in an allocated buffer and put it into PIN. */
@ -759,24 +728,28 @@ do_decipher (app_t app, const char *keyidstr,
0x84, 1, 0x81 /* Select local secret key 1 for decryption. */ 0x84, 1, 0x81 /* Select local secret key 1 for decryption. */
}; };
int rc, i; int rc, i;
int is_sigg = 0;
int fid; int fid;
if (!keyidstr || !*keyidstr || !indatalen) if (!keyidstr || !*keyidstr || !indatalen)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
rc = switch_application (app, 0);
if (rc)
return rc;
/* Check that the provided ID is valid. This is not really needed /* Check that the provided ID is valid. This is not really needed
but we do it to to enforce correct usage by the caller. */ but we do it to to enforce correct usage by the caller. */
if (!strncmp (keyidstr, "NKS-NKS3.", 9) ) if (!strncmp (keyidstr, "NKS-NKS3.", 9) )
; ;
else if (!strncmp (keyidstr, "NKS-DF01.", 9) ) else if (!strncmp (keyidstr, "NKS-DF01.", 9) )
; ;
else if (!strncmp (keyidstr, "NKS-SIGG.", 9) )
is_sigg = 1;
else else
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
keyidstr += 9; keyidstr += 9;
rc = switch_application (app, is_sigg);
if (rc)
return rc;
if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1) if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
|| !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
|| keyidstr[4]) || keyidstr[4])

View File

@ -1,3 +1,14 @@
2009-03-06 Werner Koch <wk@g10code.com>
* call-agent.c (gpgsm_agent_keyinfo, keyinfo_status_cb): New.
* keylist.c (list_cert_colon): Print card S/N.
* keylist.c (list_internal_keys): Always list ephemeral keys if
specified by keygrip or fingerprint.
(list_cert_raw): Always show ephemeral flag.
* export.c (gpgsm_export): Export ephemeral keys if specified by
keygrip.
2009-02-09 Werner Koch <wk@g10code.com> 2009-02-09 Werner Koch <wk@g10code.com>
* gpgsm.c (main): Change default cipher back to 3DES. * gpgsm.c (main): Change default cipher back to 3DES.
@ -2451,7 +2462,7 @@ h2007-11-22 Werner Koch <wk@g10code.com>
Copyright 2001, 2002, 2003, 2004, 2005, 2006, Copyright 2001, 2002, 2003, 2004, 2005, 2006,
2007, 2008 Free Software Foundation, Inc. 2007, 2008, 2009 Free Software Foundation, Inc.
This file is free software; as a special exception the author gives This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without unlimited permission to copy and/or distribute it, with or without

View File

@ -1,6 +1,6 @@
/* call-agent.c - Divert GPGSM operations to the agent /* call-agent.c - Divert GPGSM operations to the agent
* Copyright (C) 2001, 2002, 2003, 2005, 2007, * Copyright (C) 2001, 2002, 2003, 2005, 2007,
* 2008 Free Software Foundation, Inc. * 2008, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -836,3 +836,68 @@ gpgsm_agent_send_nop (ctrl_t ctrl)
} }
static int
keyinfo_status_cb (void *opaque, const char *line)
{
char **serialno = opaque;
const char *s, *s2;
if (!strncmp (line, "KEYINFO ", 8) && !*serialno)
{
s = strchr (line+8, ' ');
if (s && s[1] == 'T' && s[2] == ' ' && s[3])
{
s += 3;
s2 = strchr (s, ' ');
if ( s2 > s )
{
*serialno = xtrymalloc ((s2 - s)+1);
if (*serialno)
{
memcpy (*serialno, s, s2 - s);
(*serialno)[s2 - s] = 0;
}
}
}
}
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. */
gpg_error_t
gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno)
{
gpg_error_t err;
char line[ASSUAN_LINELENGTH];
char *serialno = NULL;
*r_serialno = NULL;
err = start_agent (ctrl);
if (err)
return err;
if (!hexkeygrip || strlen (hexkeygrip) != 40)
return gpg_error (GPG_ERR_INV_VALUE);
snprintf (line, DIM(line)-1, "KEYINFO %s", hexkeygrip);
line[DIM(line)-1] = 0;
err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL,
keyinfo_status_cb, &serialno);
if (!err && serialno)
{
/* Sanity check for bad characters. */
if (strpbrk (serialno, ":\n\r"))
err = GPG_ERR_INV_VALUE;
}
if (err)
xfree (serialno);
else
*r_serialno = serialno;
return err;
}

View File

@ -1,5 +1,5 @@
/* export.c - Export certificates and private keys. /* export.c - Export certificates and private keys.
* Copyright (C) 2002, 2003, 2004, 2007 Free Software Foundation, Inc. * Copyright (C) 2002, 2003, 2004, 2007, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -37,11 +37,11 @@
/* A table to store a fingerprint as used in a duplicates table. We /* A table to store a fingerprint as used in a duplicates table. We
don't need to hash here because a fingerprint is alrady a perfect don't need to hash here because a fingerprint is already a perfect
hash value. This we use the most significant bits to index the hash value. This we use the most significant bits to index the
table and then use a linked list for the overflow. Possible table and then use a linked list for the overflow. Possible
enhancement for very large number of certictates: Add a second enhancement for very large number of certificates: Add a second
level table and then resort to a linked list. */ level table and then resort to a linked list. */
struct duptable_s struct duptable_s
{ {
struct duptable_s *next; struct duptable_s *next;
@ -192,18 +192,16 @@ gpgsm_export (ctrl_t ctrl, strlist_t names, FILE *fp, estream_t stream)
} }
} }
/* If all specifications are done by fingerprint, we switch to /* If all specifications are done by fingerprint or keygrip, we
ephemeral mode so that _all_ currently available and matching switch to ephemeral mode so that _all_ currently available and
certificates are exported. matching certificates are exported. */
fixme: we should in this case keep a list of certificates to
avoid accidential export of duplicate certificates. */
if (names && ndesc) if (names && ndesc)
{ {
for (i=0; (i < ndesc for (i=0; (i < ndesc
&& (desc[i].mode == KEYDB_SEARCH_MODE_FPR && (desc[i].mode == KEYDB_SEARCH_MODE_FPR
|| desc[i].mode == KEYDB_SEARCH_MODE_FPR20 || desc[i].mode == KEYDB_SEARCH_MODE_FPR20
|| desc[i].mode == KEYDB_SEARCH_MODE_FPR16)); i++) || desc[i].mode == KEYDB_SEARCH_MODE_FPR16
|| desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++)
; ;
if (i == ndesc) if (i == ndesc)
keydb_set_ephemeral (hd, 1); keydb_set_ephemeral (hd, 1);
@ -228,7 +226,7 @@ gpgsm_export (ctrl_t ctrl, strlist_t names, FILE *fp, estream_t stream)
rc = insert_duptable (dtable, fpr, &exists); rc = insert_duptable (dtable, fpr, &exists);
if (rc) if (rc)
{ {
log_error ("inserting into duplicates table fauiled: %s\n", log_error ("inserting into duplicates table failed: %s\n",
gpg_strerror (rc)); gpg_strerror (rc));
goto leave; goto leave;
} }

View File

@ -196,8 +196,8 @@ gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array)
return array; return array;
} }
/* Return an allocated buffer with the keygrip of CERT in from of an /* Return an allocated buffer with the keygrip of CERT encoded as a
hexstring. NULL is returned in case of error */ hexstring. NULL is returned in case of error. */
char * char *
gpgsm_get_keygrip_hexstring (ksba_cert_t cert) gpgsm_get_keygrip_hexstring (ksba_cert_t cert)
{ {

View File

@ -395,6 +395,8 @@ int gpgsm_agent_learn (ctrl_t ctrl);
int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc); int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc);
gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc); gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc);
gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl); gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl);
gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip,
char **r_serialno);
/*-- call-dirmngr.c --*/ /*-- call-dirmngr.c --*/
int gpgsm_dirmngr_isvalid (ctrl_t ctrl, int gpgsm_dirmngr_isvalid (ctrl_t ctrl,

View File

@ -1,6 +1,6 @@
/* keylist.c - Print certificates in various formats. /* keylist.c - Print certificates in various formats.
* Copyright (C) 1998, 1999, 2000, 2001, 2003, * Copyright (C) 1998, 1999, 2000, 2001, 2003,
* 2004, 2005, 2008 Free Software Foundation, Inc. * 2004, 2005, 2008, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -481,7 +481,24 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
es_putc (':', fp); es_putc (':', fp);
/* Field 12, capabilities: */ /* Field 12, capabilities: */
print_capabilities (cert, fp); print_capabilities (cert, fp);
/* Field 13, not used: */
es_putc (':', fp); es_putc (':', fp);
if (have_secret)
{
char *cardsn;
p = gpgsm_get_keygrip_hexstring (cert);
if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) && cardsn)
{
/* Field 14, not used: */
es_putc (':', fp);
/* Field 15: Token serial number. */
es_fputs (cardsn, fp);
es_putc (':', fp);
}
xfree (cardsn);
xfree (p);
}
es_putc ('\n', fp); es_putc ('\n', fp);
/* FPR record */ /* FPR record */
@ -989,7 +1006,7 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err)); es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err));
} }
if (opt.with_ephemeral_keys && hd) if (hd)
{ {
unsigned int blobflags; unsigned int blobflags;
@ -1275,6 +1292,7 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
gpg_error_t rc = 0; gpg_error_t rc = 0;
const char *lastresname, *resname; const char *lastresname, *resname;
int have_secret; int have_secret;
int want_ephemeral = opt.with_ephemeral_keys;
hd = keydb_new (0); hd = keydb_new (0);
if (!hd) if (!hd)
@ -1319,7 +1337,24 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
} }
if (opt.with_ephemeral_keys) /* If all specifications are done by fingerprint or keygrip, we
switch to ephemeral mode so that _all_ currently available and
matching certificates are listed. */
if (!want_ephemeral && names && ndesc)
{
int i;
for (i=0; (i < ndesc
&& (desc[i].mode == KEYDB_SEARCH_MODE_FPR
|| desc[i].mode == KEYDB_SEARCH_MODE_FPR20
|| desc[i].mode == KEYDB_SEARCH_MODE_FPR16
|| desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++)
;
if (i == ndesc)
want_ephemeral = 1;
}
if (want_ephemeral)
keydb_set_ephemeral (hd, 1); keydb_set_ephemeral (hd, 1);
/* It would be nice to see which of the given users did actually /* It would be nice to see which of the given users did actually