New gpg-agent command to list key information.
authorWerner Koch <wk@gnupg.org>
Fri, 6 Mar 2009 17:31:27 +0000 (17:31 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 6 Mar 2009 17:31:27 +0000 (17:31 +0000)
Gpgsm does now print the S/N of cards.
Consider ephemeral keys during listing an export.

20 files changed:
NEWS
agent/ChangeLog
agent/agent.h
agent/command.c
agent/divert-scd.c
agent/findkey.c
agent/protect.c
common/ChangeLog
common/sexputil.c
common/util.h
doc/gpgsm.texi
kbx/keybox-search.c
scd/ChangeLog
scd/app-nks.c
sm/ChangeLog
sm/call-agent.c
sm/export.c
sm/fingerprint.c
sm/gpgsm.h
sm/keylist.c

diff --git a/NEWS b/NEWS
index afeba68..bfa343b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,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)
 -------------------------------------------------
index e016377..cae70f3 100644 (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>
 
        * 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
index 0e2cc9f..fa2c61d 100644 (file)
@@ -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 --*/
index 9ebcd09..ba0f8fc 100644 (file)
@@ -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)
 }
 
 
+\f
+/* 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;
+}
 
 
 \f
@@ -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 },
index a583f1a..fd8c28b 100644 (file)
 #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);
index 0bb6afd..5bea198 100644 (file)
@@ -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;
+}
index ebb02ac..8b022ec 100644 (file)
@@ -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;
+}
+
index 61ae679..f5ba7d7 100644 (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>
 
        * exechelp.c (do_exec): Make sure that /dev/null connected FDs are
index 4907a93..4ff7b49 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
  *
 #include "util.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
    public key parameters expressed in a way depended on the algorithm.
 
index 66569e2..d117f86 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
  *
@@ -183,6 +183,8 @@ gpg_error_t b64dec_finish (struct b64state *state);
 
 
 /*-- 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,
                                      unsigned char *grip);
 int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b);
index b4c92bb..659d546 100644 (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
 @option{--armor} option a few informational lines are prepended before
 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
-binary export (i.e. without using @option{armor}) works only for the
-export of one certificate.  Thus it is required to specify a
-@var{pattern} which yields exactly one certificate.
+upon way to pack more than one certificate into an ASN.1 structure,
+the binary export (i.e. without using @option{armor}) works only for
+the export of one certificate.  Thus it is required to specify a
+@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}
 @opindex export
@@ -601,7 +603,9 @@ forth to @var{epoch} which is the number of seconds elapsed since the year
 
 @item --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}
 @opindex debug-level
index 927399d..08b59e6 100644 (file)
@@ -457,7 +457,7 @@ blob_cmp_mail (KEYBOXBLOB blob, const char *name, size_t namelen, int substr)
 
 #ifdef KEYBOX_WITH_X509
 /* 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
    instead of failing a search for invalid certificates etc.  */
 static int
index b1ec747..efc888f 100644 (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>
 
        * app-openpgp.c (verify_a_chv): Remove special case for keypads.
index 6aa2056..1a520bb 100644 (file)
@@ -308,17 +308,20 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
 
 
 
-
-static gpg_error_t
-do_learn_status (app_t app, ctrl_t ctrl)
+static void
+do_learn_status_core (app_t app, ctrl_t ctrl, int is_sigg)
 {
   gpg_error_t err;
   char ct_buf[100], id_buf[100];
   int i;
+  const char *tag;
 
-  err = switch_application (app, 0);
-  if (err)
-    return err;
+  if (is_sigg)
+    tag = "SIGG";
+  else if (app->app_local->nks_version < 3)
+    tag = "DF01";
+  else
+    tag = "NKS3";
 
   /* Output information about all useful objects in the NKS application. */
   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)
         continue;
 
-      if (filelist[i].is_sigg)
+      if (!!filelist[i].is_sigg != !!is_sigg)
         continue;
 
       if (filelist[i].certtype)
@@ -342,8 +345,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
                  read that many bytes. */
               snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype);
               snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X", 
-                        app->app_local->nks_version < 3? "DF01":"NKS3",
-                        filelist[i].fid);
+                        tag, filelist[i].fid);
               send_status_info (ctrl, "CERTINFO",
                                 ct_buf, strlen (ct_buf), 
                                 id_buf, strlen (id_buf), 
@@ -361,8 +363,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
           else
             {
               snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
-                        app->app_local->nks_version < 3? "DF01":"NKS3",
-                        filelist[i].fid);
+                        tag, filelist[i].fid);
               send_status_info (ctrl, "KEYPAIRINFO",
                                 gripstr, 40, 
                                 id_buf, strlen (id_buf), 
@@ -371,58 +372,26 @@ do_learn_status (app_t app, ctrl_t ctrl)
         }
     }
 
-  err = switch_application (app, 1);
-  if (err)
-    return 0;  /* Silently ignore if we can't swicth 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;
+static gpg_error_t
+do_learn_status (app_t app, ctrl_t ctrl)
+{
+  gpg_error_t err;
 
-          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 = switch_application (app, 0);
+  if (err)
+    return err;
+  
+  do_learn_status_core (app, ctrl, 0);
 
-          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);
-            }
-        }
-    }
+  err = switch_application (app, 1);
+  if (err)
+    return 0;  /* Silently ignore if we can't switch to SigG.  */
 
+  do_learn_status_core (app, ctrl, 1);
 
   return 0;
 }
@@ -446,20 +415,24 @@ do_readcert (app_t app, const char *certid,
   int class, tag, constructed, ndef;
   size_t totobjlen, objlen, hdrlen;
   int rootca = 0;
+  int is_sigg = 0;
 
   *cert = NULL;
   *certlen = 0;
 
-  err = switch_application (app, 0);
-  if (err)
-    return err;
-
   if (!strncmp (certid, "NKS-NKS3.", 9)) 
     ;
   else if (!strncmp (certid, "NKS-DF01.", 9)) 
     ;
+  else if (!strncmp (certid, "NKS-SIGG.", 9)) 
+    is_sigg = 1;
   else
     return gpg_error (GPG_ERR_INV_ID);
+
+  err = switch_application (app, is_sigg);
+  if (err)
+    return err;
+
   certid += 9;
   if (!hexdigitp (certid) || !hexdigitp (certid+1)
       || !hexdigitp (certid+2) || !hexdigitp (certid+3) 
@@ -603,9 +576,7 @@ verify_pin (app_t app, int pwid, const char *desc,
   if (!opt.disable_keypad
       && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
     {
-      rc = pincb (pincb_arg,
-                  _("||Please enter your PIN at the reader's keypad"),
-                  NULL);
+      rc = pincb (pincb_arg, desc, NULL);
       if (rc)
         {
           log_info (_("PIN callback returned error: %s\n"),
@@ -613,11 +584,8 @@ verify_pin (app_t app, int pwid, const char *desc,
           return rc;
         }
  
-      /* Although it is possible to use a local PIN, we use the global
-         PIN for this application.  */
-      rc = iso7816_verify_kp (app->slot, 0, "", 0, &pininfo); 
-      /* Dismiss the prompt. */
-      pincb (pincb_arg, NULL, NULL);
+      rc = iso7816_verify_kp (app->slot, pwid, "", 0, &pininfo); 
+      pincb (pincb_arg, NULL, NULL);  /* Dismiss the prompt. */
     }
   else
     {
@@ -630,8 +598,6 @@ verify_pin (app_t app, int pwid, const char *desc,
           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);
       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,
       0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
   int rc, i;
+  int is_sigg = 0;
   int fid;
   unsigned char data[35];   /* Must be large enough for a SHA-1 digest
                                + 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)
     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
      but we do it to enforce correct usage by the caller. */
   if (!strncmp (keyidstr, "NKS-NKS3.", 9) ) 
     ;
   else if (!strncmp (keyidstr, "NKS-DF01.", 9) ) 
     ;
+  else if (!strncmp (keyidstr, "NKS-SIGG.", 9) ) 
+    is_sigg = 1;
   else
     return gpg_error (GPG_ERR_INV_ID);
   keyidstr += 9;
+
+  rc = switch_application (app, is_sigg);
+  if (rc)
+    return rc;
+
   if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
       || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) 
       || 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.
    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.  */
@@ -759,24 +728,28 @@ do_decipher (app_t app, const char *keyidstr,
     0x84, 1, 0x81  /* Select local secret key 1 for decryption. */
   };
   int rc, i;
+  int is_sigg = 0;
   int fid;
 
   if (!keyidstr || !*keyidstr || !indatalen)
     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
      but we do it to to enforce correct usage by the caller. */
   if (!strncmp (keyidstr, "NKS-NKS3.", 9) ) 
     ;
   else if (!strncmp (keyidstr, "NKS-DF01.", 9) ) 
     ;
+  else if (!strncmp (keyidstr, "NKS-SIGG.", 9) ) 
+    is_sigg = 1;
   else
     return gpg_error (GPG_ERR_INV_ID);
   keyidstr += 9;
+
+  rc = switch_application (app, is_sigg);
+  if (rc)
+    return rc;
+
   if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
       || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) 
       || keyidstr[4])
index 83f4569..4d98dc5 100644 (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>
 
        * 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,
-          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
  unlimited permission to copy and/or distribute it, with or without
index 0add44a..a6ac6da 100644 (file)
@@ -1,6 +1,6 @@
 /* call-agent.c - Divert GPGSM operations to the agent
  * Copyright (C) 2001, 2002, 2003, 2005, 2007,
- *               2008 Free Software Foundation, Inc.
+ *               2008, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -836,3 +836,68 @@ gpgsm_agent_send_nop (ctrl_t ctrl)
 }
 
 
+\f
+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;
+}
+
index fa2e9de..f8e23ce 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
  *
 
 
 /* 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
    table and then use a linked list for the overflow.  Possible
-   enhancement for very large number of certictates: Add a second
-   level table and then resort to a linked list. */
+   enhancement for very large number of certificates:  Add a second
+   level table and then resort to a linked list.  */
 struct duptable_s
 {
   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
-     ephemeral mode so that _all_ currently available and matching
-     certificates are exported. 
-
-     fixme: we should in this case keep a list of certificates to
-     avoid accidential export of duplicate certificates. */
+  /* If all specifications are done by fingerprint or keygrip, we
+     switch to ephemeral mode so that _all_ currently available and
+     matching certificates are exported.  */
   if (names && ndesc)
     {
       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)); i++)
+                     || desc[i].mode == KEYDB_SEARCH_MODE_FPR16
+                     || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++)
         ;
       if (i == ndesc)
         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);
       if (rc)
         {
-          log_error ("inserting into duplicates table fauiled: %s\n",
+          log_error ("inserting into duplicates table failed: %s\n",
                      gpg_strerror (rc));
           goto leave;
         }
index 6581688..addf562 100644 (file)
@@ -196,8 +196,8 @@ gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array)
   return array;
 }
 
-/* Return an allocated buffer with the keygrip of CERT in from of an
-   hexstring.  NULL is returned in case of error */
+/* Return an allocated buffer with the keygrip of CERT encoded as a
+   hexstring.  NULL is returned in case of error */
 char *
 gpgsm_get_keygrip_hexstring (ksba_cert_t cert)
 {
index 1460dc0..984dda1 100644 (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);
 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_keyinfo (ctrl_t ctrl, const char *hexkeygrip,
+                                 char **r_serialno);
 
 /*-- call-dirmngr.c --*/
 int gpgsm_dirmngr_isvalid (ctrl_t ctrl,
index b0d2f56..39b82c2 100644 (file)
@@ -1,6 +1,6 @@
 /* keylist.c - Print certificates in various formats.
  * 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.
  *
@@ -481,7 +481,24 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
   es_putc (':', fp);
   /* Field 12, capabilities: */ 
   print_capabilities (cert, fp);
+  /* Field 13, not used: */
   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);
 
   /* 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));
     }
 
-  if (opt.with_ephemeral_keys && hd)
+  if (hd)
     {
       unsigned int blobflags;
 
@@ -1275,6 +1292,7 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
   gpg_error_t rc = 0;
   const char *lastresname, *resname;
   int have_secret;
+  int want_ephemeral = opt.with_ephemeral_keys;
 
   hd = keydb_new (0);
   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);
 
   /* It would be nice to see which of the given users did actually