Add new option --with-keygrip
[gnupg.git] / sm / keylist.c
index e9056b6..4f876ff 100644 (file)
@@ -1,12 +1,12 @@
-/* keylist.c
+/* keylist.c - Print certificates in various formats.
  * Copyright (C) 1998, 1999, 2000, 2001, 2003,
- *               2004 Free Software Foundation, Inc.
+ *               2004, 2005, 2008, 2009 Free Software Foundation, Inc.
  *
  * 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 2 of the License, or
+ * 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,
@@ -15,8 +15,7 @@
  * 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include "keydb.h"
 #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
 #include "i18n.h"
+#include "tlv.h"
 
-struct list_external_parm_s {
+struct list_external_parm_s 
+{
   ctrl_t ctrl;
-  FILE *fp;
+  estream_t fp;
   int print_header;
   int with_colons;
   int with_chain;
@@ -49,7 +50,8 @@ struct list_external_parm_s {
 
 /* This table is to map Extended Key Usage OIDs to human readable
    names.  */
-struct {
+struct
+{
   const char *oid;
   const char *name;
 } key_purpose_map[] = {
@@ -66,15 +68,28 @@ struct {
   { "1.3.6.1.5.5.7.3.11", "sbgpCertAAServerAuth" },
   { "1.3.6.1.5.5.7.3.13", "eapOverPPP" },
   { "1.3.6.1.5.5.7.3.14", "wlanSSID" },       
+
+  { "2.16.840.1.113730.4.1", "serverGatedCrypto.ns" }, /* Netscape. */
+  { "1.3.6.1.4.1.311.10.3.3", "serverGatedCrypto.ms"}, /* Microsoft. */
+
+  { "1.3.6.1.5.5.7.48.1.5", "ocspNoCheck" },
+
   { NULL, NULL }
 };
 
 
+/* Do not print this extension in the list of extensions.  This is set
+   for oids which are already available via ksba fucntions. */
+#define OID_FLAG_SKIP 1
+/* The extension is a simple UTF8String and should be printed.  */
+#define OID_FLAG_UTF8 2 
+
 /* A table mapping OIDs to a descriptive string. */
-static struct {
+static struct 
+{
   char *oid;
   char *name;
-  unsigned int flag;
+  unsigned int flag; /* A flag as described above.  */
 } oidtranstbl[] = {
 
   /* Algorithms. */
@@ -107,6 +122,10 @@ static struct {
   { "0.2.262.1.10.12.4", "telesecCRLFilteredExt" },
   { "0.2.262.1.10.12.5", "telesecCRLFilterExt"},
   { "0.2.262.1.10.12.6", "telesecNamingAuthorityExt" },
+#define OIDSTR_restriction \
+    "1.3.36.8.3.8"
+  { OIDSTR_restriction,      "restriction", OID_FLAG_UTF8 },
+
 
   /* PKIX private extensions. */
   { "1.3.6.1.5.5.7.1.1", "authorityInfoAccess" },
@@ -121,13 +140,18 @@ static struct {
   { "1.3.6.1.5.5.7.1.10", "acProxying" },
   { "1.3.6.1.5.5.7.1.11", "subjectInfoAccess" },
 
+  { "1.3.6.1.5.5.7.48.1", "ocsp" },
+  { "1.3.6.1.5.5.7.48.2", "caIssuers" },
+  { "1.3.6.1.5.5.7.48.3", "timeStamping" },
+  { "1.3.6.1.5.5.7.48.5", "caRepository" },
+
   /* X.509 id-ce */
-  { "2.5.29.14", "subjectKeyIdentifier"},
-  { "2.5.29.15", "keyUsage", },
+  { "2.5.29.14", "subjectKeyIdentifier", OID_FLAG_SKIP},
+  { "2.5.29.15", "keyUsage", OID_FLAG_SKIP},
   { "2.5.29.16", "privateKeyUsagePeriod" },
-  { "2.5.29.17", "subjectAltName", },
-  { "2.5.29.18", "issuerAltName", },
-  { "2.5.29.19", "basicConstraints", 1},
+  { "2.5.29.17", "subjectAltName", OID_FLAG_SKIP},
+  { "2.5.29.18", "issuerAltName", OID_FLAG_SKIP},
+  { "2.5.29.19", "basicConstraints", OID_FLAG_SKIP},
   { "2.5.29.20", "cRLNumber" },
   { "2.5.29.21", "cRLReason" },
   { "2.5.29.22", "expirationDate" },
@@ -137,13 +161,13 @@ static struct {
   { "2.5.29.28", "issuingDistributionPoint" },
   { "2.5.29.29", "certificateIssuer" },
   { "2.5.29.30", "nameConstraints" },
-  { "2.5.29.31", "cRLDistributionPoints", },
-  { "2.5.29.32", "certificatePolicies", },
+  { "2.5.29.31", "cRLDistributionPoints", OID_FLAG_SKIP},
+  { "2.5.29.32", "certificatePolicies", OID_FLAG_SKIP},
   { "2.5.29.32.0", "anyPolicy" },
   { "2.5.29.33", "policyMappings" },
-  { "2.5.29.35", "authorityKeyIdentifier", },
+  { "2.5.29.35", "authorityKeyIdentifier", OID_FLAG_SKIP},
   { "2.5.29.36", "policyConstraints" },
-  { "2.5.29.37", "extKeyUsage", },
+  { "2.5.29.37", "extKeyUsage", OID_FLAG_SKIP},
   { "2.5.29.46", "freshestCRL" },
   { "2.5.29.54", "inhibitAnyPolicy" },
 
@@ -160,6 +184,12 @@ static struct {
   { "2.16.840.1.113730.1.12", "netscape-ssl-server-name" },
   { "2.16.840.1.113730.1.13", "netscape-comment" },
 
+  /* GnuPG extensions */
+  { "1.3.6.1.4.1.11591.2.1.1", "pkaAddress" },
+
+  /* Extensions used by the Bundesnetzagentur.  */
+  { "1.3.6.1.4.1.8301.3.5", "validityModel" },
+
   { NULL }
 };
 
@@ -186,7 +216,7 @@ get_oid_desc (const char *oid, unsigned int *flag)
 
 
 static void
-print_key_data (ksba_cert_t cert, FILE *fp)
+print_key_data (ksba_cert_t cert, estream_t fp)
 {
 #if 0  
   int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0;
@@ -194,29 +224,47 @@ print_key_data (ksba_cert_t cert, FILE *fp)
 
   for(i=0; i < n; i++ ) 
     {
-      fprintf (fp, "pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) );
+      es_fprintf (fp, "pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) );
       mpi_print(stdout, pk->pkey[i], 1 );
       putchar(':');
       putchar('\n');
     }
+#else
+  (void)cert;
+  (void)fp;
 #endif
 }
 
 static void
-print_capabilities (ksba_cert_t cert, FILE *fp)
+print_capabilities (ksba_cert_t cert, estream_t fp)
 {
   gpg_error_t err;
   unsigned int use;
+  size_t buflen;
+  char buffer[1];
+
+  err = ksba_cert_get_user_data (cert, "is_qualified", 
+                                 &buffer, sizeof (buffer), &buflen);
+  if (!err && buflen)
+    {
+      if (*buffer)
+        es_putc ('q', fp);
+    }    
+  else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+    ; /* Don't know - will not get marked as 'q' */
+  else
+    log_debug ("get_user_data(is_qualified) failed: %s\n",
+               gpg_strerror (err)); 
 
   err = ksba_cert_get_key_usage (cert, &use);
   if (gpg_err_code (err) == GPG_ERR_NO_DATA)
     {
-      putc ('e', fp);
-      putc ('s', fp);
-      putc ('c', fp);
-      putc ('E', fp);
-      putc ('S', fp);
-      putc ('C', fp);
+      es_putc ('e', fp);
+      es_putc ('s', fp);
+      es_putc ('c', fp);
+      es_putc ('E', fp);
+      es_putc ('S', fp);
+      es_putc ('C', fp);
       return;
     }
   if (err)
@@ -227,58 +275,72 @@ print_capabilities (ksba_cert_t cert, FILE *fp)
     } 
 
   if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT)))
-    putc ('e', fp);
+    es_putc ('e', fp);
   if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
-    putc ('s', fp);
+    es_putc ('s', fp);
   if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN))
-    putc ('c', fp);
+    es_putc ('c', fp);
   if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT)))
-    putc ('E', fp);
+    es_putc ('E', fp);
   if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
-    putc ('S', fp);
+    es_putc ('S', fp);
   if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN))
-    putc ('C', fp);
+    es_putc ('C', fp);
+
+  es_putc (':', fp);
 }
 
 
 static void
-print_time (gnupg_isotime_t t, FILE *fp)
+print_time (gnupg_isotime_t t, estream_t fp)
 {
   if (!t || !*t)
     ;
   else 
-    fputs (t, fp);
+    es_fputs (t, fp);
 }
 
 
-/* return an allocated string with the email address extracted from a
-   DN */
+/* Return an allocated string with the email address extracted from a
+   DN.  Note hat we use this code also in ../kbx/keybox-blob.c.  */
 static char *
 email_kludge (const char *name)
 {
-  const unsigned char *p;
+  const char *p, *string;
   unsigned char *buf;
   int n;
 
-  if (strncmp (name, "1.2.840.113549.1.9.1=#", 22))
-    return NULL;
+  string = name;
+  for (;;)
+    {
+      p = strstr (string, "1.2.840.113549.1.9.1=#");
+      if (!p)
+        return NULL;
+      if (p == name || (p > string+1 && p[-1] == ',' && p[-2] != '\\'))
+        {
+          name = p + 22;
+          break;
+        }
+      string = p + 22;
+    }
+
+
   /* This looks pretty much like an email address in the subject's DN
      we use this to add an additional user ID entry.  This way,
-     openSSL generated keys get a nicer and usable listing */
-  name += 22;    
+     OpenSSL generated keys get a nicer and usable listing.  */
   for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++)
     ;
-  if (*p != '#' || !n)
+  if (!n)
     return NULL;
   buf = xtrymalloc (n+3);
   if (!buf)
     return NULL; /* oops, out of core */
   *buf = '<';
-  for (n=1, p=name; *p != '#'; p +=2, n++)
+  for (n=1, p=name; hexdigitp (p); p +=2, n++)
     buf[n] = xtoi_2 (p);
   buf[n++] = '>';
   buf[n] = 0;
-  return buf;
+  return (char*)buf;
 }
 
 
@@ -287,8 +349,9 @@ email_kludge (const char *name)
 /* List one certificate in colon mode */
 static void
 list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
-                 FILE *fp, int have_secret)
+                 estream_t fp, int have_secret)
 {
+  int rc;
   int idx;
   char truststring[2];
   char *p;
@@ -298,13 +361,44 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
   gpg_error_t valerr;
   int algo;
   unsigned int nbits;
+  const char *chain_id;
+  char *chain_id_buffer = NULL;
+  int is_root = 0;
+  char *kludge_uid;
 
   if (ctrl->with_validation)
-    valerr = gpgsm_validate_chain (ctrl, cert, NULL, 1, NULL, 0);
+    valerr = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, NULL, 0, NULL);
   else
     valerr = 0;
 
-  fputs (have_secret? "crs:":"crt:", fp);
+
+  /* We need to get the fingerprint and the chaining ID in advance. */
+  fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+  {
+    ksba_cert_t next;
+
+    rc = gpgsm_walk_cert_chain (ctrl, cert, &next);
+    if (!rc) /* We known the issuer's certificate. */
+      {
+        p = gpgsm_get_fingerprint_hexstring (next, GCRY_MD_SHA1);
+        chain_id_buffer = p;
+        chain_id = chain_id_buffer;
+        ksba_cert_release (next);
+      }
+    else if (rc == -1)  /* We have reached the root certificate. */
+      {
+        chain_id = fpr;
+        is_root = 1;
+      }
+    else
+      chain_id = NULL;
+  }
+
+
+  es_fputs (have_secret? "crs:":"crt:", fp);
+
+  /* Note: We can't use multiple flags, like "ei", because the
+     validation check does only return one error.  */
   truststring[0] = 0;
   truststring[1] = 0;
   if ((validity & VALIDITY_REVOKED)
@@ -312,8 +406,6 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
     *truststring = 'r';
   else if (gpg_err_code (valerr) == GPG_ERR_CERT_EXPIRED)
     *truststring = 'e';
-  else if (valerr)
-    *truststring = 'i';
   else 
     {
       /* Lets also check whether the certificate under question
@@ -326,22 +418,40 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
           && !ksba_cert_get_validity (cert, 1, not_after)
           && *not_after && strcmp (current_time, not_after) > 0 )
         *truststring = 'e';
+      else if (valerr)
+        *truststring = 'i';
+      else if (ctrl->with_validation && !is_root)
+        *truststring = 'f';
+    }
+
+  /* If we have no truststring yet (i.e. the certificate might be
+     good) and this is a root certificate, we ask the agent whether
+     this is a trusted root certificate. */
+  if (!*truststring && is_root)
+    {
+      struct rootca_flags_s dummy_flags;
+
+      rc = gpgsm_agent_istrusted (ctrl, cert, NULL, &dummy_flags);
+      if (!rc)
+        *truststring = 'u';  /* Yes, we trust this one (ultimately). */
+      else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED)
+        *truststring = 'n';  /* No, we do not trust this one. */
+      /* (in case of an error we can't tell anything.) */
     }
   
   if (*truststring)
-    fputs (truststring, fp);
+    es_fputs (truststring, fp);
 
-  fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
   algo = gpgsm_get_key_algo_info (cert, &nbits);
-  fprintf (fp, ":%u:%d:%s:", nbits, algo, fpr+24);
+  es_fprintf (fp, ":%u:%d:%s:", nbits, algo, fpr+24);
 
   /* We assume --fixed-list-mode for gpgsm */
   ksba_cert_get_validity (cert, 0, t);
   print_time (t, fp);
-  putc (':', fp);
+  es_putc (':', fp);
   ksba_cert_get_validity (cert, 1, t);
   print_time ( t, fp);
-  putc (':', fp);
+  es_putc (':', fp);
   /* Field 8, serial number: */
   if ((sexp = ksba_cert_get_serial (cert)))
     {
@@ -355,102 +465,112 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
             len = len*10 + atoi_1 (s);
           if (*s == ':')
             for (s++; len; len--, s++)
-              fprintf (fp,"%02X", *s);
+              es_fprintf (fp,"%02X", *s);
         }
       xfree (sexp);
     }
-  putc (':', fp);
+  es_putc (':', fp);
   /* Field 9, ownertrust - not used here */
-  putc (':', fp);
+  es_putc (':', fp);
   /* field 10, old user ID - we use it here for the issuer DN */
   if ((p = ksba_cert_get_issuer (cert,0)))
     {
-      print_sanitized_string (fp, p, ':');
+      es_write_sanitized (fp, p, strlen (p), ":", NULL);
       xfree (p);
     }
-  putc (':', fp);
+  es_putc (':', fp);
   /* Field 11, signature class - not used */ 
-  putc (':', fp);
+  es_putc (':', fp);
   /* Field 12, capabilities: */ 
   print_capabilities (cert, fp);
-  putc (':', fp);
-  putc ('\n', 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 */
-  fprintf (fp, "fpr:::::::::%s:::", fpr);
+  es_fprintf (fp, "fpr:::::::::%s:::", fpr);
   /* Print chaining ID (field 13)*/
-  {
-    ksba_cert_t next;
-    int rc;
-    
-    rc = gpgsm_walk_cert_chain (cert, &next);
-    if (!rc) /* We known the issuer's certificate. */
-      {
-        p = gpgsm_get_fingerprint_hexstring (next, GCRY_MD_SHA1);
-        fputs (p, fp);
-        xfree (p);
-        ksba_cert_release (next);
-      }
-    else if (rc == -1)  /* We reached the root certificate. */
-      {
-        fputs (fpr, fp);
-      }
-  }
-  putc (':', fp);
-  putc ('\n', fp);
-  xfree (fpr); fpr = NULL;
-
+  if (chain_id)
+    es_fputs (chain_id, fp);
+  es_putc (':', fp);
+  es_putc ('\n', fp);
+  xfree (fpr); fpr = NULL; chain_id = NULL;
+  xfree (chain_id_buffer); chain_id_buffer = NULL;
 
   if (opt.with_key_data)
     {
       if ( (p = gpgsm_get_keygrip_hexstring (cert)))
         {
-          fprintf (fp, "grp:::::::::%s:\n", p);
+          es_fprintf (fp, "grp:::::::::%s:\n", p);
           xfree (p);
         }
       print_key_data (cert, fp);
     }
 
+  kludge_uid = NULL;
   for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++)
     {
-      fprintf (fp, "uid:%s::::::::", truststring);
-      print_sanitized_string (fp, p, ':');
-      putc (':', fp);
-      putc (':', fp);
-      putc ('\n', fp);
+      /* In the case that the same email address is in the subject DN
+         as well as in an alternate subject name we avoid printing it
+         a second time. */
+      if (kludge_uid && !strcmp (kludge_uid, p))
+        continue;
+
+      es_fprintf (fp, "uid:%s::::::::", truststring);
+      es_write_sanitized (fp, p, strlen (p), ":", NULL);
+      es_putc (':', fp);
+      es_putc (':', fp);
+      es_putc ('\n', fp);
       if (!idx)
         {
           /* It would be better to get the faked email address from
              the keydb.  But as long as we don't have a way to pass
              the meta data back, we just check it the same way as the
              code used to create the keybox meta data does */
-          char *pp = email_kludge (p);
-          if (pp)
+          kludge_uid = email_kludge (p);
+          if (kludge_uid)
             {
-              fprintf (fp, "uid:%s::::::::", truststring);
-              print_sanitized_string (fp, pp, ':');
-              putc (':', fp);
-              putc (':', fp);
-              putc ('\n', fp);
-              xfree (pp);
+              es_fprintf (fp, "uid:%s::::::::", truststring);
+              es_write_sanitized (fp, kludge_uid, strlen (kludge_uid),
+                                  ":", NULL);
+              es_putc (':', fp);
+              es_putc (':', fp);
+              es_putc ('\n', fp);
             }
         }
       xfree (p);
     }
+  xfree (kludge_uid);
 }
 
 
 static void
-print_name_raw (FILE *fp, const char *string)
+print_name_raw (estream_t fp, const char *string)
 {
   if (!string)
-    fputs ("[error]", fp);
+    es_fputs ("[error]", fp);
   else
-    print_sanitized_string (fp, string, 0);
+    es_write_sanitized (fp, string, strlen (string), NULL, NULL);
 }
 
 static void
-print_names_raw (FILE *fp, int indent, ksba_name_t name)
+print_names_raw (estream_t fp, int indent, ksba_name_t name)
 {
   int idx;
   const char *s;
@@ -461,29 +581,85 @@ print_names_raw (FILE *fp, int indent, ksba_name_t name)
 
   if (!name)
     {
-      fputs ("none\n", fp);
+      es_fputs ("none\n", fp);
       return;
     }
   
   for (idx=0; (s = ksba_name_enum (name, idx)); idx++)
     {
       char *p = ksba_name_get_uri (name, idx);
-      printf ("%*s%s\n", idx||indent_all?indent:0, "", p?p:s);
+      es_fprintf (fp, "%*s", idx||indent_all?indent:0, "");
+      es_write_sanitized (fp, p?p:s, strlen (p?p:s), NULL, NULL);
+      es_putc ('\n', fp);
       xfree (p);
     }
 }
 
 
+static void
+print_utf8_extn_raw (estream_t fp, int indent, 
+                     const unsigned char *der, size_t derlen)
+{
+  gpg_error_t err;
+  int class, tag, constructed, ndef;
+  size_t objlen, hdrlen;
+
+  if (indent < 0)
+    indent = - indent;
+
+  err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
+                          &ndef, &objlen, &hdrlen);
+  if (!err && (objlen > derlen || tag != TAG_UTF8_STRING))
+    err = gpg_error (GPG_ERR_INV_OBJ);
+  if (err)
+    {
+      es_fprintf (fp, "%*s[%s]\n", indent, "", gpg_strerror (err));
+      return;
+    }
+  es_fprintf (fp, "%*s(%.*s)\n", indent, "", (int)objlen, der);
+}
+
+
+static void
+print_utf8_extn (estream_t fp, int indent, 
+                 const unsigned char *der, size_t derlen)
+{
+  gpg_error_t err;
+  int class, tag, constructed, ndef;
+  size_t objlen, hdrlen;
+  int indent_all;
+
+  if ((indent_all = (indent < 0)))
+    indent = - indent;
+
+  err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
+                          &ndef, &objlen, &hdrlen);
+  if (!err && (objlen > derlen || tag != TAG_UTF8_STRING))
+    err = gpg_error (GPG_ERR_INV_OBJ);
+  if (err)
+    {
+      es_fprintf (fp, "%*s[%s%s]\n",
+                  indent_all? indent:0, "", _("Error - "), gpg_strerror (err));
+      return;
+    }
+  es_fprintf (fp, "%*s\"", indent_all? indent:0, "");
+  /* Fixme: we should implement word wrapping */
+  es_write_sanitized (fp, der, objlen, "\"", NULL);
+  es_fputs ("\"\n", fp);
+}
+
+
 /* List one certificate in raw mode useful to have a closer look at
-   the certificate.  This one does not beautification and only minimal
+   the certificate.  This one does no beautification and only minimal
    output sanitation.  It is mainly useful for debugging. */
 static void
-list_cert_raw (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
+list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
+               ksba_cert_t cert, estream_t fp, int have_secret,
                int with_validation)
 {
   gpg_error_t err;
   size_t off, len;
-  ksba_sexp_t sexp;
+  ksba_sexp_t sexp, keyid;
   char *dn;
   ksba_isotime_t t;
   int idx, i;
@@ -493,125 +669,165 @@ list_cert_raw (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
   const char *oid, *s;
   ksba_name_t name, name2;
   unsigned int reason;
+  const unsigned char *cert_der = NULL;
+
+  (void)have_secret;
+
+  es_fprintf (fp, "           ID: 0x%08lX\n",
+              gpgsm_get_short_fingerprint (cert, NULL));
 
   sexp = ksba_cert_get_serial (cert);
-  fputs ("Serial number: ", fp);
+  es_fputs ("          S/N: ", fp);
   gpgsm_print_serial (fp, sexp);
   ksba_free (sexp);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
 
   dn = ksba_cert_get_issuer (cert, 0);
-  fputs ("       Issuer: ", fp);
+  es_fputs ("       Issuer: ", fp);
   print_name_raw (fp, dn);
   ksba_free (dn);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
   for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++)
     {
-      fputs ("          aka: ", fp);
+      es_fputs ("          aka: ", fp);
       print_name_raw (fp, dn);
       ksba_free (dn);
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   dn = ksba_cert_get_subject (cert, 0);
-  fputs ("      Subject: ", fp);
+  es_fputs ("      Subject: ", fp);
   print_name_raw (fp, dn);
   ksba_free (dn);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
   for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++)
     {
-      fputs ("          aka: ", fp);
+      es_fputs ("          aka: ", fp);
       print_name_raw (fp, dn);
       ksba_free (dn);
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   dn = gpgsm_get_fingerprint_string (cert, 0);
-  fprintf (fp, "     sha1_fpr: %s\n", dn?dn:"error");
+  es_fprintf (fp, "     sha1_fpr: %s\n", dn?dn:"error");
   xfree (dn);
 
   dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5);
-  fprintf (fp, "      md5_fpr: %s\n", dn?dn:"error");
+  es_fprintf (fp, "      md5_fpr: %s\n", dn?dn:"error");
+  xfree (dn);
+
+  dn = gpgsm_get_certid (cert);
+  es_fprintf (fp, "       certid: %s\n", dn?dn:"error");
+  xfree (dn);
+
+  dn = gpgsm_get_keygrip_hexstring (cert);
+  es_fprintf (fp, "      keygrip: %s\n", dn?dn:"error");
   xfree (dn);
 
   ksba_cert_get_validity (cert, 0, t);
-  fputs ("    notBefore: ", fp);
+  es_fputs ("    notBefore: ", fp);
   gpgsm_print_time (fp, t);
-  putc ('\n', fp);
-  fputs ("     notAfter: ", fp);
+  es_putc ('\n', fp);
+  es_fputs ("     notAfter: ", fp);
   ksba_cert_get_validity (cert, 1, t);
   gpgsm_print_time (fp, t);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
 
   oid = ksba_cert_get_digest_algo (cert);
   s = get_oid_desc (oid, NULL);
-  fprintf (fp, "     hashAlgo: %s%s%s%s\n", oid, s?" (":"",s?s:"",s?")":"");
+  es_fprintf (fp, "     hashAlgo: %s%s%s%s\n", oid, s?" (":"",s?s:"",s?")":"");
 
   {
     const char *algoname;
     unsigned int nbits;
 
     algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits));
-    fprintf (fp, "      keyType: %u bit %s\n",  nbits, algoname? algoname:"?");
+    es_fprintf (fp, "      keyType: %u bit %s\n",
+                nbits, algoname? algoname:"?");
   }
 
+  /* subjectKeyIdentifier */
+  es_fputs ("    subjKeyId: ", fp);
+  err = ksba_cert_get_subj_key_id (cert, NULL, &keyid);
+  if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
+    {
+      if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+        es_fputs ("[none]\n", fp);
+      else
+        {
+          gpgsm_print_serial (fp, keyid);
+          ksba_free (keyid);
+          es_putc ('\n', fp);
+        }
+    }
+  else
+    es_fputs ("[?]\n", fp);
+
+
   /* authorityKeyIdentifier */
-  fputs ("    authKeyId: ", fp);
-  err = ksba_cert_get_auth_key_id (cert, NULL, &name, &sexp);
+  es_fputs ("    authKeyId: ", fp);
+  err = ksba_cert_get_auth_key_id (cert, &keyid, &name, &sexp);
   if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
     {
       if (gpg_err_code (err) == GPG_ERR_NO_DATA || !name)
-        fputs ("[none]\n", fp);
+        es_fputs ("[none]\n", fp);
       else
         {
           gpgsm_print_serial (fp, sexp);
           ksba_free (sexp);
-          putc ('\n', fp);
+          es_putc ('\n', fp);
           print_names_raw (fp, -15, name);
           ksba_name_release (name);
         }
+      if (keyid)
+        {
+          es_fputs (" authKeyId.ki: ", fp);
+          gpgsm_print_serial (fp, keyid);
+          ksba_free (keyid);
+          es_putc ('\n', fp);
+        }
     }
   else
-    fputs ("[?]\n", fp);
+    es_fputs ("[?]\n", fp);
 
-  fputs ("     keyUsage:", fp);
+  es_fputs ("     keyUsage:", fp);
   err = ksba_cert_get_key_usage (cert, &kusage);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA)
     {
       if (err)
-        fprintf (fp, " [error: %s]", gpg_strerror (err));
+        es_fprintf (fp, " [error: %s]", gpg_strerror (err));
       else
         {
           if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE))
-            fputs (" digitalSignature", fp);
+            es_fputs (" digitalSignature", fp);
           if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION))  
-            fputs (" nonRepudiation", fp);
+            es_fputs (" nonRepudiation", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) 
-            fputs (" keyEncipherment", fp);
+            es_fputs (" keyEncipherment", fp);
           if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT))
-            fputs (" dataEncipherment", fp);
+            es_fputs (" dataEncipherment", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT))    
-            fputs (" keyAgreement", fp);
+            es_fputs (" keyAgreement", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN))
-            fputs (" certSign", fp);
+            es_fputs (" certSign", fp);
           if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN))  
-            fputs (" crlSign", fp);
+            es_fputs (" crlSign", fp);
           if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY))
-            fputs (" encipherOnly", fp);
+            es_fputs (" encipherOnly", fp);
           if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY))  
-            fputs (" decipherOnly", fp);
+            es_fputs (" decipherOnly", fp);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
   else
-    fputs ("[none]\n", fp);
+    es_fputs (" [none]\n", fp);
 
-  fputs ("  extKeyUsage: ", fp);
+  es_fputs ("  extKeyUsage: ", fp);
   err = ksba_cert_get_ext_key_usages (cert, &string);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA)
     { 
       if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else
         {
           p = string;
@@ -621,30 +837,30 @@ list_cert_raw (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
               for (i=0; key_purpose_map[i].oid; i++)
                 if ( !strcmp (key_purpose_map[i].oid, p) )
                   break;
-              fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp);
+              es_fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp);
               p = pend;
               if (*p != 'C')
-                fputs (" (suggested)", fp);
+                es_fputs (" (suggested)", fp);
               if ((p = strchr (p, '\n')))
                 {
                   p++;
-                  fputs ("\n               ", fp);
+                  es_fputs ("\n               ", fp);
                 }
             }
           xfree (string);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
   else
-    fputs ("[none]\n", fp);
+    es_fputs ("[none]\n", fp);
 
 
-  fputs ("     policies: ", fp);
+  es_fputs ("     policies: ", fp);
   err = ksba_cert_get_cert_policies (cert, &string);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA)
     {
       if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else
         {
           p = string;
@@ -654,106 +870,111 @@ list_cert_raw (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
               for (i=0; key_purpose_map[i].oid; i++)
                 if ( !strcmp (key_purpose_map[i].oid, p) )
                   break;
-              fputs (p, fp);
+              es_fputs (p, fp);
               p = pend;
               if (*p == 'C')
-                fputs (" (critical)", fp);
+                es_fputs (" (critical)", fp);
               if ((p = strchr (p, '\n')))
                 {
                   p++;
-                  fputs ("\n               ", fp);
+                  es_fputs ("\n               ", fp);
                 }
             }
           xfree (string);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
   else
-    fputs ("[none]\n", fp);
+    es_fputs ("[none]\n", fp);
 
-  fputs ("  chainLength: ", fp);
+  es_fputs ("  chainLength: ", fp);
   err = ksba_cert_is_ca (cert, &is_ca, &chainlen);
   if (err || is_ca)
     {
-      if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+      if (gpg_err_code (err) == GPG_ERR_NO_VALUE )
+        es_fprintf (fp, "[none]");
+      else if (err)
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else if (chainlen == -1)
-        fputs ("unlimited", fp);
+        es_fputs ("unlimited", fp);
       else
-        fprintf (fp, "%d", chainlen);
-      putc ('\n', fp);
+        es_fprintf (fp, "%d", chainlen);
+      es_putc ('\n', fp);
     }
   else
-    fputs ("not a CA\n", fp);
+    es_fputs ("not a CA\n", fp);
 
 
   /* CRL distribution point */
   for (idx=0; !(err=ksba_cert_get_crl_dist_point (cert, idx, &name, &name2,
                                                   &reason)) ;idx++)
     {
-      fputs ("        crlDP: ", fp);
+      es_fputs ("        crlDP: ", fp);
       print_names_raw (fp, 15, name);
       if (reason)
         {
-          fputs ("               reason: ", fp);
+          es_fputs ("               reason: ", fp);
           if ( (reason & KSBA_CRLREASON_UNSPECIFIED))
-            fputs (" unused", stdout);
+            es_fputs (" unused", fp);
           if ( (reason & KSBA_CRLREASON_KEY_COMPROMISE))
-            fputs (" keyCompromise", stdout);
+            es_fputs (" keyCompromise", fp);
           if ( (reason & KSBA_CRLREASON_CA_COMPROMISE))
-            fputs (" caCompromise", stdout);
+            es_fputs (" caCompromise", fp);
           if ( (reason & KSBA_CRLREASON_AFFILIATION_CHANGED))
-            fputs (" affiliationChanged", stdout);
+            es_fputs (" affiliationChanged", fp);
           if ( (reason & KSBA_CRLREASON_SUPERSEDED))
-            fputs (" superseded", stdout);
+            es_fputs (" superseded", fp);
           if ( (reason & KSBA_CRLREASON_CESSATION_OF_OPERATION))
-            fputs (" cessationOfOperation", stdout);
+            es_fputs (" cessationOfOperation", fp);
           if ( (reason & KSBA_CRLREASON_CERTIFICATE_HOLD))
-            fputs (" certificateHold", stdout);
-          putchar ('\n');
+            es_fputs (" certificateHold", fp);
+          es_putc ('\n', fp);
         }
-      fputs ("               issuer: ", fp);
+      es_fputs ("               issuer: ", fp);
       print_names_raw (fp, 23, name2);
       ksba_name_release (name);
       ksba_name_release (name2);
     }
-  if (err && gpg_err_code (err) != GPG_ERR_EOF)
-    fputs ("        crlDP: [error]\n", fp);
+  if (err && gpg_err_code (err) != GPG_ERR_EOF
+      && gpg_err_code (err) != GPG_ERR_NO_VALUE)
+    es_fputs ("        crlDP: [error]\n", fp);
   else if (!idx)
-    fputs ("        crlDP: [none]\n", fp);
+    es_fputs ("        crlDP: [none]\n", fp);
 
 
   /* authorityInfoAccess. */
   for (idx=0; !(err=ksba_cert_get_authority_info_access (cert, idx, &string,
                                                          &name)); idx++)
     {
-      fputs ("     authInfo: ", fp);
+      es_fputs ("     authInfo: ", fp);
       s = get_oid_desc (string, NULL);
-      fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":"");
+      es_fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":"");
       print_names_raw (fp, -15, name);
       ksba_name_release (name);
       ksba_free (string);
     }
-  if (err && gpg_err_code (err) != GPG_ERR_EOF)
-    fputs ("     authInfo: [error]\n", fp);
+  if (err && gpg_err_code (err) != GPG_ERR_EOF
+      && gpg_err_code (err) != GPG_ERR_NO_VALUE)
+    es_fputs ("     authInfo: [error]\n", fp);
   else if (!idx)
-    fputs ("     authInfo: [none]\n", fp);
+    es_fputs ("     authInfo: [none]\n", fp);
 
   /* subjectInfoAccess. */
   for (idx=0; !(err=ksba_cert_get_subject_info_access (cert, idx, &string,
                                                          &name)); idx++)
     {
-      fputs ("  subjectInfo: ", fp);
+      es_fputs ("  subjectInfo: ", fp);
       s = get_oid_desc (string, NULL);
-      fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":"");
+      es_fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":"");
       print_names_raw (fp, -15, name);
       ksba_name_release (name);
       ksba_free (string);
     }
-  if (err && gpg_err_code (err) != GPG_ERR_EOF)
-    fputs ("     subjInfo: [error]\n", fp);
+  if (err && gpg_err_code (err) != GPG_ERR_EOF
+      && gpg_err_code (err) != GPG_ERR_NO_VALUE)
+    es_fputs ("     subjInfo: [error]\n", fp);
   else if (!idx)
-    fputs ("     subjInfo: [none]\n", fp);
+    es_fputs ("     subjInfo: [none]\n", fp);
 
 
   for (idx=0; !(err=ksba_cert_get_extension (cert, idx,
@@ -762,22 +983,42 @@ list_cert_raw (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
       unsigned int flag;
 
       s = get_oid_desc (oid, &flag);
+      if ((flag & OID_FLAG_SKIP))
+        continue;
 
-      if (!(flag & 1))
-        fprintf (fp, "     %s: %s%s%s%s  [%d octets]\n",
-                 i? "critExtn":"    extn",
-                 oid, s?" (":"", s?s:"", s?")":"", (int)len);
+      es_fprintf (fp, "     %s: %s%s%s%s  [%d octets]\n",
+                  i? "critExtn":"    extn",
+                  oid, s?" (":"", s?s:"", s?")":"", (int)len);
+      if ((flag & OID_FLAG_UTF8))
+        {
+          if (!cert_der)
+            cert_der = ksba_cert_get_image (cert, NULL);
+          assert (cert_der);
+          print_utf8_extn_raw (fp, -15, cert_der+off, len);
+        }
     }
 
 
   if (with_validation)
     {
-      err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0);
+      err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL);
       if (!err)
-        fprintf (fp, "  [certificate is good]\n");
+        es_fprintf (fp, "  [certificate is good]\n");
       else
-        fprintf (fp, "  [certificate is bad: %s]\n", gpg_strerror (err));
+        es_fprintf (fp, "  [certificate is bad: %s]\n", gpg_strerror (err));
+    }
+
+  if (hd)
+    {
+      unsigned int blobflags;
+
+      err = keydb_get_flags (hd, KEYBOX_FLAG_BLOB, 0, &blobflags);
+      if (err)
+        es_fprintf (fp, "  [error getting keyflags: %s]\n",gpg_strerror (err));
+      else if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
+        es_fprintf (fp, "  [stored as ephemeral]\n");
     }
+
 }
 
 
@@ -785,7 +1026,7 @@ list_cert_raw (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
 
 /* List one certificate in standard mode */
 static void
-list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
+list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret,
                int with_validation)
 {
   gpg_error_t err;
@@ -796,46 +1037,53 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
   int is_ca, chainlen;
   unsigned int kusage;
   char *string, *p, *pend;
+  size_t off, len;
+  const char *oid;
+  const unsigned char *cert_der = NULL;
+
+
+  es_fprintf (fp, "           ID: 0x%08lX\n",
+              gpgsm_get_short_fingerprint (cert, NULL));
 
   sexp = ksba_cert_get_serial (cert);
-  fputs ("Serial number: ", fp);
+  es_fputs ("          S/N: ", fp);
   gpgsm_print_serial (fp, sexp);
   ksba_free (sexp);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
 
   dn = ksba_cert_get_issuer (cert, 0);
-  fputs ("       Issuer: ", fp);
-  gpgsm_print_name (fp, dn);
+  es_fputs ("       Issuer: ", fp);
+  gpgsm_es_print_name (fp, dn);
   ksba_free (dn);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
   for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++)
     {
-      fputs ("          aka: ", fp);
-      gpgsm_print_name (fp, dn);
+      es_fputs ("          aka: ", fp);
+      gpgsm_es_print_name (fp, dn);
       ksba_free (dn);
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   dn = ksba_cert_get_subject (cert, 0);
-  fputs ("      Subject: ", fp);
-  gpgsm_print_name (fp, dn);
+  es_fputs ("      Subject: ", fp);
+  gpgsm_es_print_name (fp, dn);
   ksba_free (dn);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
   for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++)
     {
-      fputs ("          aka: ", fp);
-      gpgsm_print_name (fp, dn);
+      es_fputs ("          aka: ", fp);
+      gpgsm_es_print_name (fp, dn);
       ksba_free (dn);
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   ksba_cert_get_validity (cert, 0, t);
-  fputs ("     validity: ", fp);
+  es_fputs ("     validity: ", fp);
   gpgsm_print_time (fp, t);
-  fputs (" through ", fp);
+  es_fputs (" through ", fp);
   ksba_cert_get_validity (cert, 1, t);
   gpgsm_print_time (fp, t);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
 
 
   {
@@ -843,46 +1091,47 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
     unsigned int nbits;
 
     algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits));
-    fprintf (fp, "     key type: %u bit %s\n", nbits, algoname? algoname:"?");
+    es_fprintf (fp, "     key type: %u bit %s\n",
+                nbits, algoname? algoname:"?");
   }
 
 
   err = ksba_cert_get_key_usage (cert, &kusage);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA)
     {
-      fputs ("    key usage:", fp);
+      es_fputs ("    key usage:", fp);
       if (err)
-        fprintf (fp, " [error: %s]", gpg_strerror (err));
+        es_fprintf (fp, " [error: %s]", gpg_strerror (err));
       else
         {
           if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE))
-            fputs (" digitalSignature", fp);
+            es_fputs (" digitalSignature", fp);
           if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION))  
-            fputs (" nonRepudiation", fp);
+            es_fputs (" nonRepudiation", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) 
-            fputs (" keyEncipherment", fp);
+            es_fputs (" keyEncipherment", fp);
           if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT))
-            fputs (" dataEncipherment", fp);
+            es_fputs (" dataEncipherment", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT))    
-            fputs (" keyAgreement", fp);
+            es_fputs (" keyAgreement", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN))
-            fputs (" certSign", fp);
+            es_fputs (" certSign", fp);
           if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN))  
-            fputs (" crlSign", fp);
+            es_fputs (" crlSign", fp);
           if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY))
-            fputs (" encipherOnly", fp);
+            es_fputs (" encipherOnly", fp);
           if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY))  
-            fputs (" decipherOnly", fp);
+            es_fputs (" decipherOnly", fp);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   err = ksba_cert_get_ext_key_usages (cert, &string);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA)
     { 
-      fputs ("ext key usage: ", fp);
+      es_fputs ("ext key usage: ", fp);
       if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else
         {
           p = string;
@@ -892,27 +1141,42 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
               for (i=0; key_purpose_map[i].oid; i++)
                 if ( !strcmp (key_purpose_map[i].oid, p) )
                   break;
-              fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp);
+              es_fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp);
               p = pend;
               if (*p != 'C')
-                fputs (" (suggested)", fp);
+                es_fputs (" (suggested)", fp);
               if ((p = strchr (p, '\n')))
                 {
                   p++;
-                  fputs (", ", fp);
+                  es_fputs (", ", fp);
                 }
             }
           xfree (string);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
+  /* Print restrictions.  */
+  for (idx=0; !(err=ksba_cert_get_extension (cert, idx,
+                                             &oid, NULL, &off, &len));idx++)
+    {
+      if (!strcmp (oid, OIDSTR_restriction) )
+        {
+          if (!cert_der)
+            cert_der = ksba_cert_get_image (cert, NULL);
+          assert (cert_der);
+          es_fputs ("  restriction: ", fp);
+          print_utf8_extn (fp, 15, cert_der+off, len);
+        }
+    }
+
+  /* Print policies.  */
   err = ksba_cert_get_cert_policies (cert, &string);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA)
     {
-      fputs ("     policies: ", fp);
+      es_fputs ("     policies: ", fp);
       if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else
         {
           for (p=string; *p; p++)
@@ -920,71 +1184,112 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
               if (*p == '\n')
                 *p = ',';
             }
-          print_sanitized_string (fp, string, 0);
+          es_write_sanitized (fp, string, strlen (string), NULL, NULL);
           xfree (string);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   err = ksba_cert_is_ca (cert, &is_ca, &chainlen);
   if (err || is_ca)
     {
-      fputs (" chain length: ", fp);
-      if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+      es_fputs (" chain length: ", fp);
+      if (gpg_err_code (err) == GPG_ERR_NO_VALUE )
+        es_fprintf (fp, "none");
+      else if (err)
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else if (chainlen == -1)
-        fputs ("unlimited", fp);
+        es_fputs ("unlimited", fp);
       else
-        fprintf (fp, "%d", chainlen);
-      putc ('\n', fp);
+        es_fprintf (fp, "%d", chainlen);
+      es_putc ('\n', fp);
     }
 
   if (opt.with_md5_fingerprint)
     {
       dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5);
-      fprintf (fp, "      md5 fpr: %s\n", dn?dn:"error");
+      es_fprintf (fp, "      md5 fpr: %s\n", dn?dn:"error");
       xfree (dn);
     }
 
   dn = gpgsm_get_fingerprint_string (cert, 0);
-  fprintf (fp, "  fingerprint: %s\n", dn?dn:"error");
+  es_fprintf (fp, "  fingerprint: %s\n", dn?dn:"error");
   xfree (dn);
 
+  if (opt.with_keygrip)
+    {
+      dn = gpgsm_get_keygrip_hexstring (cert);
+      if (dn)
+        {
+          es_fprintf (fp, "      keygrip: %s\n", dn);
+          xfree (dn);
+        }
+    }      
+
+  if (have_secret)
+    {
+      char *cardsn;
+
+      p = gpgsm_get_keygrip_hexstring (cert);
+      if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) && cardsn)
+        es_fprintf (fp, "     card s/n: %s\n", cardsn);
+      xfree (cardsn);
+      xfree (p);
+    }
+
   if (with_validation)
     {
-      err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0);
+      gpg_error_t tmperr;
+      size_t buflen;
+      char buffer[1];
+      
+      err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL);
+      tmperr = ksba_cert_get_user_data (cert, "is_qualified", 
+                                        &buffer, sizeof (buffer), &buflen);
+      if (!tmperr && buflen)
+        {
+          if (*buffer)
+            es_fputs ("  [qualified]\n", fp);
+        }    
+      else if (gpg_err_code (tmperr) == GPG_ERR_NOT_FOUND)
+        ; /* Don't know - will not get marked as 'q' */
+      else
+        log_debug ("get_user_data(is_qualified) failed: %s\n",
+                   gpg_strerror (tmperr)); 
+
       if (!err)
-        fprintf (fp, "  [certificate is good]\n");
+        es_fprintf (fp, "  [certificate is good]\n");
       else
-        fprintf (fp, "  [certificate is bad: %s]\n", gpg_strerror (err));
+        es_fprintf (fp, "  [certificate is bad: %s]\n", gpg_strerror (err));
     }
 }
 
 
 /* Same as standard mode mode list all certifying certs too. */
 static void
-list_cert_chain (ctrl_t ctrl, ksba_cert_t cert, int raw_mode,
-                 FILE *fp, int with_validation)
+list_cert_chain (ctrl_t ctrl, KEYDB_HANDLE hd,
+                 ksba_cert_t cert, int raw_mode,
+                 estream_t fp, int with_validation)
 {
   ksba_cert_t next = NULL;
 
   if (raw_mode)
-    list_cert_raw (ctrl, cert, fp, 0, with_validation);
+    list_cert_raw (ctrl, hd, cert, fp, 0, with_validation);
   else
     list_cert_std (ctrl, cert, fp, 0, with_validation);
   ksba_cert_ref (cert);
-  while (!gpgsm_walk_cert_chain (cert, &next))
+  while (!gpgsm_walk_cert_chain (ctrl, cert, &next))
     {
       ksba_cert_release (cert);
-      fputs ("Certified by\n", fp);
+      es_fputs ("Certified by\n", fp);
       if (raw_mode)
-        list_cert_raw (ctrl, next, fp, 0, with_validation);
+        list_cert_raw (ctrl, hd, next, fp, 0, with_validation);
       else
         list_cert_std (ctrl, next, fp, 0, with_validation);
       cert = next;
     }
   ksba_cert_release (cert);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
 }
 
 
@@ -992,20 +1297,22 @@ list_cert_chain (ctrl_t ctrl, ksba_cert_t cert, int raw_mode,
 /* List all internal keys or just the keys given as NAMES.  MODE is a
    bit vector to specify what keys are to be included; see
    gpgsm_list_keys (below) for details.  If RAW_MODE is true, the raw
-   output mode will be used intead of the standard beautified one.
+   output mode will be used instead of the standard beautified one.
  */
 static gpg_error_t
-list_internal_keys (ctrl_t ctrl, STRLIST names, FILE *fp,
+list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
                     unsigned int mode, int raw_mode)
 {
   KEYDB_HANDLE hd;
   KEYDB_SEARCH_DESC *desc = NULL;
-  STRLIST sl;
+  strlist_t sl;
   int ndesc;
   ksba_cert_t cert = NULL;
+  ksba_cert_t lastcert = NULL;
   gpg_error_t rc = 0;
   const char *lastresname, *resname;
   int have_secret;
+  int want_ephemeral = ctrl->with_ephemeral_keys;
 
   hd = keydb_new (0);
   if (!hd)
@@ -1026,7 +1333,7 @@ list_internal_keys (ctrl_t ctrl, STRLIST names, FILE *fp,
   desc = xtrycalloc (ndesc, sizeof *desc);
   if (!ndesc)
     {
-      rc = gpg_error_from_errno (errno);
+      rc = gpg_error_from_syserror ();
       log_error ("out of core\n");
       goto leave;
     }
@@ -1037,7 +1344,7 @@ list_internal_keys (ctrl_t ctrl, STRLIST names, FILE *fp,
     {
       for (ndesc=0, sl=names; sl; sl = sl->next) 
         {
-          rc = keydb_classify_name (sl->d, desc+ndesc);
+          rc = classify_user_id (sl->d, desc+ndesc);
           if (rc)
             {
               log_error ("key `%s' not found: %s\n",
@@ -1050,6 +1357,26 @@ list_internal_keys (ctrl_t ctrl, STRLIST names, FILE *fp,
       
     }
 
+  /* 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
      match one in the keyring.  To implement this we need to have a
      found flag for each entry in desc and to set this we must check
@@ -1057,6 +1384,7 @@ list_internal_keys (ctrl_t ctrl, STRLIST names, FILE *fp,
      currently we stop at the first match.  To do this we need an
      extra flag to enable this feature so */
 
+  /* Suppress duplicates at least when they follow each other.  */
   lastresname = NULL;
   while (!(rc = keydb_search (hd, desc, ndesc)))
     {
@@ -1077,7 +1405,16 @@ list_internal_keys (ctrl_t ctrl, STRLIST names, FILE *fp,
           log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc));
           goto leave;
         }
-      
+      /* Skip duplicated certificates, at least if they follow each
+        others.  This works best if a single key is searched for and
+        expected.  FIXME: Non-sequential duplicates remain.  */
+      if (gpgsm_certs_identical_p (cert, lastcert))
+       {
+         ksba_cert_release (cert);
+          cert = NULL;
+         continue;
+       }
+
       resname = keydb_get_resource_name (hd);
       
       if (lastresname != resname ) 
@@ -1086,10 +1423,10 @@ list_internal_keys (ctrl_t ctrl, STRLIST names, FILE *fp,
           
           if (ctrl->no_server)
             {
-              fprintf (fp, "%s\n", resname );
+              es_fprintf (fp, "%s\n", resname );
               for (i=strlen(resname); i; i-- )
-                putchar('-');
-              putc ('\n', fp);
+                es_putc ('-', fp);
+              es_putc ('\n', fp);
               lastresname = resname;
             }
         }
@@ -1100,7 +1437,7 @@ list_internal_keys (ctrl_t ctrl, STRLIST names, FILE *fp,
           char *p = gpgsm_get_keygrip_hexstring (cert);
           if (p)
             {
-              rc = gpgsm_agent_havekey (ctrl, p);
+              rc = gpgsm_agent_havekey (ctrl, p); 
               if (!rc)
                 have_secret = 1;
               else if ( gpg_err_code (rc) != GPG_ERR_NO_SECKEY)
@@ -1117,19 +1454,22 @@ list_internal_keys (ctrl_t ctrl, STRLIST names, FILE *fp,
           if (ctrl->with_colons)
             list_cert_colon (ctrl, cert, validity, fp, have_secret);
           else if (ctrl->with_chain)
-            list_cert_chain (ctrl, cert, raw_mode, fp, ctrl->with_validation);
+            list_cert_chain (ctrl, hd, cert,
+                             raw_mode, fp, ctrl->with_validation);
           else
             {
               if (raw_mode)
-                list_cert_raw (ctrl, cert, fp, have_secret,
+                list_cert_raw (ctrl, hd, cert, fp, have_secret,
                                ctrl->with_validation);
               else
                 list_cert_std (ctrl, cert, fp, have_secret,
                                ctrl->with_validation);
-              putc ('\n', fp);
+              es_putc ('\n', fp);
             }
         }
-      ksba_cert_release (cert); 
+
+      ksba_cert_release (lastcert); 
+      lastcert = cert;
       cert = NULL;
     }
   if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1 )
@@ -1139,6 +1479,7 @@ list_internal_keys (ctrl_t ctrl, STRLIST names, FILE *fp,
   
  leave:
   ksba_cert_release (cert);
+  ksba_cert_release (lastcert); 
   xfree (desc);
   keydb_release (hd);
   return rc;
@@ -1159,24 +1500,24 @@ list_external_cb (void *cb_value, ksba_cert_t cert)
       const char *resname = "[external keys]";
       int i;
 
-      fprintf (parm->fp, "%s\n", resname );
+      es_fprintf (parm->fp, "%s\n", resname );
       for (i=strlen(resname); i; i-- )
-        putchar('-');
-      putc ('\n', parm->fp);
+        es_putc('-', parm->fp);
+      es_putc ('\n', parm->fp);
       parm->print_header = 0;
     }
 
   if (parm->with_colons)
     list_cert_colon (parm->ctrl, cert, 0, parm->fp, 0);
   else if (parm->with_chain)
-    list_cert_chain (parm->ctrl, cert, parm->raw_mode, parm->fp, 0);
+    list_cert_chain (parm->ctrl, NULL, cert, parm->raw_mode, parm->fp, 0);
   else
     {
       if (parm->raw_mode)
-        list_cert_raw (parm->ctrl, cert, parm->fp, 0, 0);
+        list_cert_raw (parm->ctrl, NULL, cert, parm->fp, 0, 0);
       else
         list_cert_std (parm->ctrl, cert, parm->fp, 0, 0);
-      putc ('\n', parm->fp);
+      es_putc ('\n', parm->fp);
     }
 }
 
@@ -1185,7 +1526,7 @@ list_external_cb (void *cb_value, ksba_cert_t cert)
    make sense here because it would be unwise to list external secret
    keys */
 static gpg_error_t
-list_external_keys (CTRL ctrl, STRLIST names, FILE *fp, int raw_mode)
+list_external_keys (ctrl_t ctrl, strlist_t names, estream_t fp, int raw_mode)
 {
   int rc;
   struct list_external_parm_s parm;
@@ -1197,7 +1538,10 @@ list_external_keys (CTRL ctrl, STRLIST names, FILE *fp, int raw_mode)
   parm.with_chain = ctrl->with_chain;
   parm.raw_mode  = raw_mode;
 
-  rc = gpgsm_dirmngr_lookup (ctrl, names, list_external_cb, &parm);
+  rc = gpgsm_dirmngr_lookup (ctrl, names, 0, list_external_cb, &parm);
+  if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1 
+      || gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
+    rc = 0; /* "Not found" is not an error here. */
   if (rc)
     log_error ("listing external keys failed: %s\n", gpg_strerror (rc));
   return rc;
@@ -1215,7 +1559,8 @@ list_external_keys (CTRL ctrl, STRLIST names, FILE *fp, int raw_mode)
     Bit 8: Do a raw format dump.
  */
 gpg_error_t
-gpgsm_list_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode)
+gpgsm_list_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
+                 unsigned int mode)
 {
   gpg_error_t err = 0;