card: Print matching OpenPGP and X.509 data.
authorWerner Koch <wk@gnupg.org>
Wed, 30 Jan 2019 14:01:34 +0000 (15:01 +0100)
committerWerner Koch <wk@gnupg.org>
Wed, 30 Jan 2019 14:01:34 +0000 (15:01 +0100)
* tools/card-tool-keys.c: New.
* tools/Makefile.am (gpg_card_tool_SOURCES): Add file.
* tools/card-tool.h (struct pubkey_s, pubkey_t): New.
(struct userid_s, userid_t): New.
(struct keyblock_s, keyblock_t): New.
* common/util.h (GNUPG_PROTOCOL_): New const
* tools/gpg-card-tool.c (aTest): Add temporary command.
(list_one_kinfo): Print info from gpg and gpgsm.

Signed-off-by: Werner Koch <wk@gnupg.org>
common/util.h
tools/Makefile.am
tools/card-tool-keys.c [new file with mode: 0644]
tools/card-tool.h
tools/gpg-card-tool.c

index a4b1cbd..863f9e3 100644 (file)
@@ -262,6 +262,13 @@ void gnupg_module_name_flush_some (void);
 void gnupg_set_builddir (const char *newdir);
 
 
+/* A list of constants to identify protocols.  This is used by tools
+ * which need to distinguish between the different protocols
+ * implemented by GnuPG.  May be used as bit flags.  */
+#define GNUPG_PROTOCOL_OPENPGP    1   /* The one and only (gpg).      */
+#define GNUPG_PROTOCOL_CMS        2   /* The core of S/MIME (gpgsm)   */
+#define GNUPG_PROTOCOL_SSH_AGENT  4   /* Out ssh-agent implementation */
+
 
 /*-- gpgrlhelp.c --*/
 void gnupg_rl_initialize (void);
index f74221b..ad0f223 100644 (file)
@@ -128,6 +128,7 @@ gpg_card_tool_SOURCES   = \
        gpg-card-tool.c \
        card-tool.h     \
        card-call-scd.c \
+       card-tool-keys.c \
        card-tool-misc.c
 
 gpg_card_tool_LDADD     = ../common/libgpgrl.a $(common_libs) \
diff --git a/tools/card-tool-keys.c b/tools/card-tool-keys.c
new file mode 100644 (file)
index 0000000..af2425c
--- /dev/null
@@ -0,0 +1,467 @@
+/* card-tool-keys.c - OpenPGP and CMS related functions for gpg-card-tool
+ * Copyright (C) 2019 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/util.h"
+#include "../common/i18n.h"
+#include "../common/ccparray.h"
+#include "../common/exectool.h"
+#include "../common/openpgpdefs.h"
+#include "card-tool.h"
+
+/* Release a keyblocm object.  */
+void
+release_keyblock (keyblock_t keyblock)
+{
+  pubkey_t pubkey;
+  userid_t uid;
+
+  while (keyblock)
+    {
+      keyblock_t keyblocknext = keyblock->next;
+      pubkey = keyblock->keys;
+      while (pubkey)
+        {
+          pubkey_t pubkeynext = pubkey->next;
+          xfree (pubkey);
+          pubkey = pubkeynext;
+        }
+      uid = keyblock->uids;
+      while (uid)
+        {
+          userid_t uidnext = uid->next;
+          xfree (uid->value);
+          xfree (uid);
+          uid = uidnext;
+        }
+      xfree (keyblock);
+      keyblock = keyblocknext;
+    }
+}
+
+
+
+/* Object to communicate with the status_cb. */
+struct status_cb_s
+{
+  const char *pgm; /* Name of the program for debug purposes. */
+  int no_pubkey;   /* Result flag.  */
+};
+
+
+/* Status callback helper for the exec functions.  */
+static void
+status_cb (void *opaque, const char *keyword, char *args)
+{
+  struct status_cb_s *c = opaque;
+  const char *s;
+
+  if (DBG_EXTPROG)
+    log_debug ("%s: status: %s %s\n", c->pgm, keyword, args);
+
+  if (!strcmp (keyword, "ERROR")
+      && (s=has_leading_keyword (args, "keylist.getkey"))
+      && gpg_err_code (atoi (s)) == GPG_ERR_NO_PUBKEY)
+    {
+      /* No public key was found.  gpg terminates with an error in
+       * this case and we can't change that behaviour.  Instead we
+       * detect this status and carry that error forward. */
+      c->no_pubkey = 1;
+    }
+
+}
+
+
+/* Helper for get_matching_keys to parse "pub" style records.  */
+static gpg_error_t
+parse_key_record (char **fields, int nfields, pubkey_t *r_pubkey)
+{
+  pubkey_t pubkey;
+
+  pubkey = xtrycalloc (1, sizeof *pubkey);
+  if (!pubkey)
+    return gpg_error_from_syserror ();
+  *r_pubkey = pubkey;
+  return 0;
+}
+
+
+/* Run gpg or gpgsm to get a list of all keys matching the 20 byte
+ * KEYGRIP.  PROTOCOL is one of or a combination of
+ * GNUPG_PROTOCOL_OPENPGP and GNUPG_PROTOCOL_CMS.  On success a new
+ * keyblock is stored at R_KEYBLOCK; on error NULL is stored there. */
+gpg_error_t
+get_matching_keys (const unsigned char *keygrip, int protocol,
+                   keyblock_t *r_keyblock)
+{
+  gpg_error_t err;
+  ccparray_t ccp;
+  const char **argv;
+  estream_t listing;
+  char hexgrip[1 + (2*KEYGRIP_LEN) + 1];
+  char *line = NULL;
+  size_t length_of_line = 0;
+  size_t maxlen;
+  ssize_t len;
+  char **fields = NULL;
+  int nfields;
+  int first_seen;
+  keyblock_t keyblock_head, *keyblock_tail, kb;
+  pubkey_t pubkey, pk;
+  size_t n;
+  struct status_cb_s status_cb_parm;
+
+  *r_keyblock = NULL;
+
+  keyblock_head = NULL;
+  keyblock_tail = &keyblock_head;
+  kb = NULL;
+
+  /* Shortcut to run a listing on both protocols.  */
+  if ((protocol & GNUPG_PROTOCOL_OPENPGP) && (protocol & GNUPG_PROTOCOL_CMS))
+    {
+      err = get_matching_keys (keygrip, GNUPG_PROTOCOL_OPENPGP, &kb);
+      if (!err || gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
+        {
+          *keyblock_tail = kb;
+          keyblock_tail = &kb->next;
+          kb = NULL;
+          err = get_matching_keys (keygrip, GNUPG_PROTOCOL_CMS, &kb);
+          if (!err)
+            {
+              *keyblock_tail = kb;
+              keyblock_tail = &kb->next;
+              kb = NULL;
+            }
+          else if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
+            err = 0;
+        }
+      if (err)
+        release_keyblock (keyblock_head);
+      else
+        *r_keyblock = keyblock_head;
+      return err;
+    }
+
+  /* Check that we have only one protocol.  */
+  if (protocol != GNUPG_PROTOCOL_OPENPGP && protocol != GNUPG_PROTOCOL_CMS)
+    return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+
+  /* Open a memory stream.  */
+  listing = es_fopenmem (0, "w+b");
+  if (!listing)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
+      return err;
+    }
+
+  status_cb_parm.pgm = protocol == GNUPG_PROTOCOL_OPENPGP? "gpg":"gpgsm";
+  status_cb_parm.no_pubkey = 0;
+
+  hexgrip[0] = '&';
+  bin2hex (keygrip, KEYGRIP_LEN, hexgrip+1);
+
+  ccparray_init (&ccp, 0);
+
+  if (opt.verbose > 1 || DBG_EXTPROG)
+    ccparray_put (&ccp, "--verbose");
+  else
+    ccparray_put (&ccp, "--quiet");
+  ccparray_put (&ccp, "--no-options");
+  ccparray_put (&ccp, "--batch");
+  ccparray_put (&ccp, "--status-fd=2");
+  ccparray_put (&ccp, "--with-colons");
+  ccparray_put (&ccp, "--with-keygrip");
+  ccparray_put (&ccp, "--list-keys");
+  ccparray_put (&ccp, hexgrip);
+
+  ccparray_put (&ccp, NULL);
+  argv = ccparray_get (&ccp, NULL);
+  if (!argv)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  err = gnupg_exec_tool_stream (protocol == GNUPG_PROTOCOL_OPENPGP?
+                                opt.gpg_program : opt.gpgsm_program,
+                                argv, NULL, NULL, listing, status_cb,
+                                &status_cb_parm);
+  if (err)
+    {
+      if (status_cb_parm.no_pubkey)
+        err = gpg_error (GPG_ERR_NO_PUBKEY);
+      else if (gpg_err_code (err) != GPG_ERR_GENERAL)
+        log_error ("key listing failed: %s\n", gpg_strerror (err));
+      goto leave;
+    }
+
+  es_rewind (listing);
+  first_seen = 0;
+  maxlen = 8192; /* Set limit large enough for all escaped UIDs.  */
+  while ((len = es_read_line (listing, &line, &length_of_line, &maxlen)) > 0)
+    {
+      if (!maxlen)
+        {
+          log_error ("received line too long\n");
+          err = gpg_error (GPG_ERR_LINE_TOO_LONG);
+          goto leave;
+        }
+      /* Strip newline and carriage return, if present.  */
+      while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
+       line[--len] = '\0';
+
+      xfree (fields);
+      fields = strtokenize (line, ":");
+      if (!fields)
+        {
+          err = gpg_error_from_syserror ();
+          log_error ("strtokenize failed: %s\n", gpg_strerror (err));
+          goto leave;
+        }
+      for (nfields = 0; fields[nfields]; nfields++)
+        ;
+      if (!nfields)
+        {
+          err = gpg_error (GPG_ERR_INV_ENGINE);
+          goto leave;
+        }
+
+      /* Skip over all records until we reach a pub or sec.  */
+      if (!first_seen
+          && (!strcmp (fields[0], "pub") || !strcmp (fields[0], "sec")
+              || !strcmp (fields[0], "crt") || !strcmp (fields[0], "crs")))
+        first_seen = 1;
+      if (!first_seen)
+        continue;
+
+      if (!strcmp (fields[0], "pub") || !strcmp (fields[0], "sec")
+          || !strcmp (fields[0], "crt") || !strcmp (fields[0], "crs"))
+        {
+          if (kb)  /* Finish the current keyblock.  */
+            {
+              *keyblock_tail = kb;
+              keyblock_tail = &kb->next;
+            }
+          kb = xtrycalloc (1, sizeof *kb);
+          if (!kb)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          kb->protocol = protocol;
+          err = parse_key_record (fields, nfields, &pubkey);
+          if (err)
+            goto leave;
+          kb->keys = pubkey;
+          pubkey = NULL;
+        }
+      else if (!strcmp (fields[0], "sub") || !strcmp (fields[0], "ssb"))
+        {
+          log_assert (kb && kb->keys);
+          err = parse_key_record (fields, nfields, &pubkey);
+          if (err)
+            goto leave;
+          for (pk = kb->keys; pk->next; pk = pk->next)
+                ;
+          pk->next = pubkey;
+          pubkey = NULL;
+        }
+      else if (!strcmp (fields[0], "fpr") && nfields > 9)
+        {
+          log_assert (kb && kb->keys);
+          n = strlen (fields[9]);
+          if (n != 64 && n != 40 && n != 32)
+            {
+              log_debug ("bad length (%zu) in fpr record\n", n);
+              err = gpg_error (GPG_ERR_INV_ENGINE);
+              goto leave;
+            }
+          n /= 2;
+
+          for (pk = kb->keys; pk->next; pk = pk->next)
+            ;
+          if (pk->fprlen)
+            {
+              log_debug ("too many fpr records\n");
+              err = gpg_error (GPG_ERR_INV_ENGINE);
+              goto leave;
+            }
+          log_assert (n <= sizeof pk->fpr);
+          pk->fprlen = n;
+          if (hex2bin (fields[9], pk->fpr, n) < 0)
+            {
+              log_debug ("bad chars in fpr record\n");
+              err = gpg_error (GPG_ERR_INV_ENGINE);
+              goto leave;
+            }
+        }
+      else if (!strcmp (fields[0], "grp") && nfields > 9)
+        {
+          log_assert (kb && kb->keys);
+          n = strlen (fields[9]);
+          if (n != 2*KEYGRIP_LEN)
+            {
+              log_debug ("bad length (%zu) in grp record\n", n);
+              err = gpg_error (GPG_ERR_INV_ENGINE);
+              goto leave;
+            }
+          n /= 2;
+
+          for (pk = kb->keys; pk->next; pk = pk->next)
+            ;
+          if (pk->grip_valid)
+            {
+              log_debug ("too many grp records\n");
+              err = gpg_error (GPG_ERR_INV_ENGINE);
+              goto leave;
+            }
+          if (hex2bin (fields[9], pk->grip, KEYGRIP_LEN) < 0)
+            {
+              log_debug ("bad chars in fpr record\n");
+              err = gpg_error (GPG_ERR_INV_ENGINE);
+              goto leave;
+            }
+          pk->grip_valid = 1;
+          if (!memcmp (pk->grip, keygrip, KEYGRIP_LEN))
+            pk->requested = 1;
+        }
+      else if (!strcmp (fields[0], "uid") && nfields > 9)
+        {
+          userid_t uid, u;
+
+          uid = xtrycalloc (1, sizeof *uid);
+          if (!uid)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          uid->value = decode_c_string (fields[9]);
+          if (!uid->value)
+            {
+              err = gpg_error_from_syserror ();
+              xfree (uid);
+              goto leave;
+            }
+          if (!kb->uids)
+            kb->uids = uid;
+          else
+            {
+              for (u = kb->uids; u->next; u = u->next)
+                ;
+              u->next = uid;
+            }
+        }
+    }
+  if (len < 0 || es_ferror (listing))
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("error reading memory stream\n");
+      goto leave;
+    }
+
+  if (kb) /* Finish the current keyblock.  */
+    {
+      *keyblock_tail = kb;
+      keyblock_tail = &kb->next;
+      kb = NULL;
+    }
+
+  if (!keyblock_head)
+    err = gpg_error (GPG_ERR_NO_PUBKEY);
+
+ leave:
+  if (err)
+    release_keyblock (keyblock_head);
+  else
+    *r_keyblock = keyblock_head;
+  xfree (kb);
+  xfree (fields);
+  es_free (line);
+  xfree (argv);
+  es_fclose (listing);
+  return err;
+}
+
+
+void
+dump_keyblock (keyblock_t keyblock)
+{
+  keyblock_t kb;
+  pubkey_t pubkey;
+  userid_t uid;
+
+  for (kb = keyblock; kb; kb = kb->next)
+    {
+      log_info ("%s key:\n",
+                 kb->protocol == GNUPG_PROTOCOL_OPENPGP? "OpenPGP":"X.509");
+      for (pubkey = kb->keys; pubkey; pubkey = pubkey->next)
+        {
+          log_info ("  grip: ");
+          if (pubkey->grip_valid)
+            log_printhex (pubkey->grip, KEYGRIP_LEN, NULL);
+          log_printf ("%s\n", pubkey->requested? " (*)":"");
+
+          log_info ("   fpr: ");
+          log_printhex (pubkey->fpr, pubkey->fprlen, "");
+        }
+      for (uid = kb->uids; uid; uid = uid->next)
+        {
+          log_info ("   uid: %s\n", uid->value);
+        }
+    }
+}
+
+
+
+gpg_error_t
+test_get_matching_keys (const char *hexgrip)
+{
+  gpg_error_t err;
+  unsigned char grip[KEYGRIP_LEN];
+  keyblock_t keyblock;
+
+  if (strlen (hexgrip) != 40)
+    {
+      log_error ("error: invalid keygrip\n");
+      return 0;
+    }
+  if (hex2bin (hexgrip, grip, sizeof grip) < 0)
+    {
+      log_error ("error: bad kegrip\n");
+      return 0;
+    }
+  err = get_matching_keys (grip,
+                           (GNUPG_PROTOCOL_OPENPGP | GNUPG_PROTOCOL_CMS),
+                           &keyblock);
+  if (err)
+    {
+      log_error ("get_matching_keys failed: %s\n", gpg_strerror (err));
+      return err;
+    }
+
+  dump_keyblock (keyblock);
+  release_keyblock (keyblock);
+  return 0;
+}
index b1d8662..d502ecb 100644 (file)
@@ -50,6 +50,41 @@ struct
 #define DBG_IPC       (opt.debug & DBG_IPC_VALUE)
 #define DBG_EXTPROG   (opt.debug & DBG_EXTPROG_VALUE)
 
+/* The maximum length of a binary fingerprint.  */
+#define MAX_FINGERPRINT_LEN  32
+
+
+/*
+ * Data structures to store keyblocks (aka certificates).
+ */
+struct pubkey_s
+{
+  struct pubkey_s *next;   /* The next key.  */
+  unsigned char grip[KEYGRIP_LEN];
+  unsigned char fpr[MAX_FINGERPRINT_LEN];
+  unsigned char fprlen;     /* The used length of a FPR.  */
+  unsigned int grip_valid:1;/* The grip is valid.  */
+  unsigned int requested: 1;/* This is the requested grip.  */
+};
+typedef struct pubkey_s *pubkey_t;
+
+struct userid_s
+{
+  struct userid_s *next;
+  char *value;   /* Malloced.  */
+};
+typedef struct userid_s *userid_t;
+
+struct keyblock_s
+{
+  struct keyblock_s *next;  /* Allow to link several keyblocks.  */
+  int protocol;      /* GPGME_PROTOCOL_OPENPGP or _CMS. */
+  pubkey_t keys;     /* The key.  For OpenPGP primary + list of subkeys.  */
+  userid_t uids;     /* The list of user ids.  */
+};
+typedef struct keyblock_s *keyblock_t;
+
+
 
 /* Enumeration of the known card application types. */
 typedef enum
@@ -76,9 +111,9 @@ struct key_attr
   };
 };
 
-/* An object to store information pertaining to a key pair.  This is
- * commonly used as a linked list with all keys known for the current
- * card.  */
+/* An object to store information pertaining to a key pair as stored
+ * on a card.  This is commonly used as a linked list with all keys
+ * known for the current card.  */
 struct key_info_s
 {
   struct key_info_s *next;
@@ -144,6 +179,13 @@ struct card_info_s
 typedef struct card_info_s *card_info_t;
 
 
+/*-- card-tool-keys.c --*/
+void release_keyblock (keyblock_t keyblock);
+gpg_error_t get_matching_keys (const unsigned char *keygrip, int protocol,
+                               keyblock_t *r_keyblock);
+gpg_error_t test_get_matching_keys (const char *hexgrip);
+
+
 /*-- card-tool-misc.c --*/
 key_info_t find_kinfo (card_info_t info, const char *keyref);
 
index 4f79620..321426b 100644 (file)
@@ -69,6 +69,9 @@ enum cmd_and_opt_values
     oLCctype,
     oLCmessages,
 
+    aTest,
+
+
     oDummy
   };
 
@@ -76,6 +79,7 @@ enum cmd_and_opt_values
 /* The list of commands and options. */
 static ARGPARSE_OPTS opts[] = {
   ARGPARSE_group (300, ("@Commands:\n ")),
+  ARGPARSE_c (aTest, "test", "test command"),
 
   ARGPARSE_group (301, ("@\nOptions:\n ")),
 
@@ -227,6 +231,10 @@ parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
         case oLCctype:     opt.lc_ctype = pargs->r.ret_str; break;
         case oLCmessages:  opt.lc_messages = pargs->r.ret_str; break;
 
+        case aTest:
+          cmd = pargs->r_opt;
+          break;
+
         default: pargs->err = 2; break;
        }
     }
@@ -292,6 +300,12 @@ main (int argc, char **argv)
   /* Run the selected command.  */
   switch (cmd)
     {
+    case aTest:
+      if (!argc)
+        wrong_args ("--test KEYGRIP");
+      err = test_get_matching_keys (*argv);
+      break;
+
     default:
       interactive_loop ();
       err = 0;
@@ -580,15 +594,25 @@ mem_is_ff (const char *mem, unsigned int memlen)
 \f
 /* Helper to list a single keyref.  */
 static void
-list_one_kinfo (key_info_t kinfo, estream_t fp)
+list_one_kinfo (key_info_t firstkinfo, key_info_t kinfo, estream_t fp)
 {
-  if (kinfo)
+  gpg_error_t err;
+  keyblock_t keyblock = NULL;
+  keyblock_t kb;
+  pubkey_t pubkey;
+  userid_t uid;
+  key_info_t ki;
+  const char *s;
+
+  if (firstkinfo && kinfo)
     {
       tty_fprintf (fp, " ");
       if (mem_is_zero (kinfo->grip, sizeof kinfo->grip))
-        tty_fprintf (fp, "[none]\n");
-      else
-        print_keygrip (fp, kinfo->grip);
+        {
+          tty_fprintf (fp, "[none]\n");
+          goto leave;
+        }
+      print_keygrip (fp, kinfo->grip);
 
       if (kinfo->fprlen && kinfo->created)
         {
@@ -597,9 +621,63 @@ list_one_kinfo (key_info_t kinfo, estream_t fp)
           tty_fprintf (fp, "      created ....: %s\n",
                        isotimestamp (kinfo->created));
         }
+      err = get_matching_keys (kinfo->grip,
+                               (GNUPG_PROTOCOL_OPENPGP | GNUPG_PROTOCOL_CMS),
+                               &keyblock);
+      if (err)
+        {
+          if (gpg_err_code (err) != GPG_ERR_NO_PUBKEY)
+            tty_fprintf (fp, "      error ......: %s\n", gpg_strerror (err));
+          goto leave;
+        }
+      for (kb = keyblock; kb; kb = kb->next)
+        {
+          tty_fprintf (fp, "      used for ...: %s\n",
+                       kb->protocol == GNUPG_PROTOCOL_OPENPGP? "OpenPGP" :
+                       kb->protocol == GNUPG_PROTOCOL_CMS? "X.509" : "?");
+          pubkey = kb->keys;
+          /* If this is not the primary key print the primary key's
+           * fingerprint or a reference to it.  */
+          if (kb->protocol == GNUPG_PROTOCOL_OPENPGP)
+            {
+              tty_fprintf (fp, "        main key .:");
+              for (ki=firstkinfo; ki; ki = ki->next)
+                if (pubkey->grip_valid
+                    && !memcmp (ki->grip, pubkey->grip, KEYGRIP_LEN))
+                  break;
+              if (ki)
+                {
+                  /* Fixme: Replace mapping by a table lookup.  */
+                  if (!memcmp (kinfo->grip, pubkey->grip, KEYGRIP_LEN))
+                    s = "this";
+                  else if (!strcmp (ki->keyref, "OPENPGP.1"))
+                    s = "Signature key";
+                  else if (!strcmp (ki->keyref, "OPENPGP.2"))
+                    s = "Encryption key";
+                  else if (!strcmp (ki->keyref, "OPENPGP.3"))
+                    s = "Authentication key";
+                  else
+                    s = NULL;
+                  if (s)
+                    tty_fprintf (fp, " <%s>\n", s);
+                  else
+                    tty_fprintf (fp, " <Key %s>\n", ki->keyref);
+                }
+              else
+                print_shax_fpr (fp, pubkey->fpr, pubkey->fprlen);
+            }
+          for (uid = kb->uids; uid; uid = uid->next)
+            {
+              print_string (fp, "        user id ..: ", uid->value);
+            }
+
+        }
     }
   else
     tty_fprintf (fp, " [none]\n");
+
+ leave:
+  release_keyblock (keyblock);
 }
 
 
@@ -620,7 +698,7 @@ list_all_kinfo (card_info_t info, keyinfolabel_t labels, estream_t fp)
         {
           tty_fprintf (fp, "%s", labels[idx].label);
           kinfo = find_kinfo (info, labels[idx].keyref);
-          list_one_kinfo (kinfo, fp);
+          list_one_kinfo (info->kinfo, kinfo, fp);
           if (kinfo)
             kinfo->xflag = 1;
         }
@@ -633,7 +711,7 @@ list_all_kinfo (card_info_t info, keyinfolabel_t labels, estream_t fp)
       for (i=5+strlen (kinfo->keyref); i < 18; i++)
         tty_fprintf (fp, ".");
       tty_fprintf (fp, ":");
-      list_one_kinfo (kinfo, fp);
+      list_one_kinfo (info->kinfo, kinfo, fp);
     }
 }