Fixed aegypten bug 299
[gnupg.git] / sm / keylist.c
index a73d18e..39c4e50 100644 (file)
@@ -1,6 +1,6 @@
-/* keylist.c
+/* keylist.c - Print certificates in various formats.
  * Copyright (C) 1998, 1999, 2000, 2001, 2003,
- *               2004 Free Software Foundation, Inc.
+ *               2004, 2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -16,7 +16,8 @@
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
@@ -43,6 +44,7 @@ struct list_external_parm_s {
   int print_header;
   int with_colons;
   int with_chain;
+  int raw_mode;
 };
 
 
@@ -65,10 +67,139 @@ 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 }
 };
 
 
+/* A table mapping OIDs to a descriptive string. */
+static struct {
+  char *oid;
+  char *name;
+  unsigned int flag;
+} oidtranstbl[] = {
+
+  /* Algorithms. */
+  { "1.2.840.10040.4.1", "dsa" },
+  { "1.2.840.10040.4.3", "dsaWithSha1" },
+
+  { "1.2.840.113549.1.1.1", "rsaEncryption" },
+  { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" },
+  { "1.2.840.113549.1.1.3", "md4WithRSAEncryption" },
+  { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" },
+  { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" },
+  { "1.2.840.113549.1.1.7", "rsaOAEP" },
+  { "1.2.840.113549.1.1.8", "rsaOAEP-MGF" },
+  { "1.2.840.113549.1.1.9", "rsaOAEP-pSpecified" },
+  { "1.2.840.113549.1.1.10", "rsaPSS" },
+  { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" },
+  { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" },
+  { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" },
+
+  { "1.3.14.3.2.26", "sha1" },
+  { "1.3.14.3.2.29",  "sha-1WithRSAEncryption" },
+  { "1.3.36.3.3.1.2", "rsaSignatureWithripemd160" },
+
+
+  /* Telesec extensions. */
+  { "0.2.262.1.10.12.0", "certExtensionLiabilityLimitationExt" },
+  { "0.2.262.1.10.12.1", "telesecCertIdExt" },
+  { "0.2.262.1.10.12.2", "telesecPolicyIdentifier" },
+  { "0.2.262.1.10.12.3", "telesecPolicyQualifierID" },
+  { "0.2.262.1.10.12.4", "telesecCRLFilteredExt" },
+  { "0.2.262.1.10.12.5", "telesecCRLFilterExt"},
+  { "0.2.262.1.10.12.6", "telesecNamingAuthorityExt" },
+
+  /* PKIX private extensions. */
+  { "1.3.6.1.5.5.7.1.1", "authorityInfoAccess" },
+  { "1.3.6.1.5.5.7.1.2", "biometricInfo" },
+  { "1.3.6.1.5.5.7.1.3", "qcStatements" },
+  { "1.3.6.1.5.5.7.1.4", "acAuditIdentity" },
+  { "1.3.6.1.5.5.7.1.5", "acTargeting" },
+  { "1.3.6.1.5.5.7.1.6", "acAaControls" },
+  { "1.3.6.1.5.5.7.1.7", "sbgp-ipAddrBlock" },
+  { "1.3.6.1.5.5.7.1.8", "sbgp-autonomousSysNum" },
+  { "1.3.6.1.5.5.7.1.9", "sbgp-routerIdentifier" },
+  { "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", 1},
+  { "2.5.29.15", "keyUsage", 1 },
+  { "2.5.29.16", "privateKeyUsagePeriod" },
+  { "2.5.29.17", "subjectAltName", 1 },
+  { "2.5.29.18", "issuerAltName", 1 },
+  { "2.5.29.19", "basicConstraints", 1},
+  { "2.5.29.20", "cRLNumber" },
+  { "2.5.29.21", "cRLReason" },
+  { "2.5.29.22", "expirationDate" },
+  { "2.5.29.23", "instructionCode" }, 
+  { "2.5.29.24", "invalidityDate" },
+  { "2.5.29.27", "deltaCRLIndicator" },
+  { "2.5.29.28", "issuingDistributionPoint" },
+  { "2.5.29.29", "certificateIssuer" },
+  { "2.5.29.30", "nameConstraints" },
+  { "2.5.29.31", "cRLDistributionPoints", 1 },
+  { "2.5.29.32", "certificatePolicies", 1 },
+  { "2.5.29.32.0", "anyPolicy" },
+  { "2.5.29.33", "policyMappings" },
+  { "2.5.29.35", "authorityKeyIdentifier", 1 },
+  { "2.5.29.36", "policyConstraints" },
+  { "2.5.29.37", "extKeyUsage", 1 },
+  { "2.5.29.46", "freshestCRL" },
+  { "2.5.29.54", "inhibitAnyPolicy" },
+
+  /* Netscape certificate extensions. */
+  { "2.16.840.1.113730.1.1", "netscape-cert-type" },
+  { "2.16.840.1.113730.1.2", "netscape-base-url" },
+  { "2.16.840.1.113730.1.3", "netscape-revocation-url" },
+  { "2.16.840.1.113730.1.4", "netscape-ca-revocation-url" },
+  { "2.16.840.1.113730.1.7", "netscape-cert-renewal-url" },
+  { "2.16.840.1.113730.1.8", "netscape-ca-policy-url" },
+  { "2.16.840.1.113730.1.9", "netscape-homePage-url" },
+  { "2.16.840.1.113730.1.10", "netscape-entitylogo" },
+  { "2.16.840.1.113730.1.11", "netscape-userPicture" },
+  { "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" },
+
+  { NULL }
+};
+
+
+/* Return the description for OID; if no description is available 
+   NULL is returned. */
+static const char *
+get_oid_desc (const char *oid, unsigned int *flag)
+{
+  int i;
+
+  if (oid)
+    for (i=0; oidtranstbl[i].oid; i++)
+      if (!strcmp (oidtranstbl[i].oid, oid))
+        {
+          if (flag)
+            *flag = oidtranstbl[i].flag;
+          return oidtranstbl[i].name;
+        }
+  if (flag)
+    *flag = 0;
+  return NULL;
+}
+
+
 static void
 print_key_data (ksba_cert_t cert, FILE *fp)
 {
@@ -91,6 +222,21 @@ print_capabilities (ksba_cert_t cert, FILE *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)
+        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)
@@ -135,34 +281,46 @@ print_time (gnupg_isotime_t t, FILE *fp)
 }
 
 
-/* return an allocated string with the email address extracted from a
+/* Return an allocated string with the email address extracted from a
    DN */
 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;    
   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;
 }
 
 
@@ -173,6 +331,7 @@ static void
 list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
                  FILE *fp, int have_secret)
 {
+  int rc;
   int idx;
   char truststring[2];
   char *p;
@@ -180,13 +339,45 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
   char *fpr;
   ksba_isotime_t t;
   gpg_error_t valerr;
+  int algo;
+  unsigned int nbits;
+  const char *chain_id;
+  char *chain_id_buffer = NULL;
+  int is_root = 0;
 
   if (ctrl->with_validation)
-    valerr = gpgsm_validate_chain (ctrl, cert, NULL, 1, NULL);
+    valerr = gpgsm_validate_chain (ctrl, cert, NULL, 1, NULL, 0);
   else
     valerr = 0;
 
+
+  /* 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 (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;
+  }
+
+
   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)
@@ -194,26 +385,51 @@ 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
+         expired.  This is merely a hack until we found a proper way
+         to store the expiration flag in the keybox. */
+      ksba_isotime_t current_time, not_after;
+  
+      gnupg_get_isotime (current_time);
+      if (!opt.ignore_expiration
+          && !ksba_cert_get_validity (cert, 1, not_after)
+          && *not_after && strcmp (current_time, not_after) > 0 )
+        *truststring = 'e';
+      else if (valerr)
+        *truststring = 'i';
+    }
+
+  /* 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, &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);
 
-  fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
-  fprintf (fp, ":%u:%d:%s:",
-           /*keylen_of_cert (cert)*/1024,
-           /* pubkey_algo_of_cert (cert)*/1,
-           fpr+24);
+  algo = gpgsm_get_key_algo_info (cert, &nbits);
+  fprintf (fp, ":%u:%d:%s:", nbits, algo, fpr+24);
 
-  /* we assume --fixed-list-mode for gpgsm */
+  /* We assume --fixed-list-mode for gpgsm */
   ksba_cert_get_validity (cert, 0, t);
   print_time (t, fp);
   putc (':', fp);
   ksba_cert_get_validity (cert, 1, t);
   print_time ( t, fp);
   putc (':', fp);
-  /* field 8, serial number: */
+  /* Field 8, serial number: */
   if ((sexp = ksba_cert_get_serial (cert)))
     {
       int len;
@@ -231,7 +447,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
       xfree (sexp);
     }
   putc (':', fp);
-  /* field 9, ownertrust - not used here */
+  /* Field 9, ownertrust - not used here */
   putc (':', fp);
   /* field 10, old user ID - we use it here for the issuer DN */
   if ((p = ksba_cert_get_issuer (cert,0)))
@@ -240,31 +456,22 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
       xfree (p);
     }
   putc (':', fp);
-  /* field 11, signature class - not used */ 
+  /* Field 11, signature class - not used */ 
   putc (':', fp);
-  /* field 12, capabilities: */ 
+  /* Field 12, capabilities: */ 
   print_capabilities (cert, fp);
   putc (':', fp);
   putc ('\n', fp);
 
   /* FPR record */
   fprintf (fp, "fpr:::::::::%s:::", fpr);
-  xfree (fpr); fpr = NULL;
-  /* print chaining ID (field 13)*/
-  {
-    ksba_cert_t next;
-    
-    if (!gpgsm_walk_cert_chain (cert, &next))
-      {
-        p = gpgsm_get_fingerprint_hexstring (next, GCRY_MD_SHA1);
-        fputs (p, fp);
-        xfree (p);
-        ksba_cert_release (next);
-      }
-  }
+  /* Print chaining ID (field 13)*/
+  if (chain_id)
+    fputs (chain_id, fp);
   putc (':', fp);
   putc ('\n', fp);
-
+  xfree (fpr); fpr = NULL; chain_id = NULL;
+  xfree (chain_id_buffer); chain_id_buffer = NULL;
 
   if (opt.with_key_data)
     {
@@ -305,6 +512,397 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
 }
 
 
+static void
+print_name_raw (FILE *fp, const char *string)
+{
+  if (!string)
+    fputs ("[error]", fp);
+  else
+    print_sanitized_string (fp, string, 0);
+}
+
+static void
+print_names_raw (FILE *fp, int indent, ksba_name_t name)
+{
+  int idx;
+  const char *s;
+  int indent_all;
+
+  if ((indent_all = (indent < 0)))
+    indent = - indent;
+
+  if (!name)
+    {
+      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", idx||indent_all?indent:0, "");
+      print_sanitized_string (fp, p?p:s, 0);
+      putc ('\n', fp);
+      xfree (p);
+    }
+}
+
+
+/* List one certificate in raw mode useful to have a closer look at
+   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, KEYDB_HANDLE hd,
+               ksba_cert_t cert, FILE *fp, int have_secret,
+               int with_validation)
+{
+  gpg_error_t err;
+  size_t off, len;
+  ksba_sexp_t sexp, keyid;
+  char *dn;
+  ksba_isotime_t t;
+  int idx, i;
+  int is_ca, chainlen;
+  unsigned int kusage;
+  char *string, *p, *pend;
+  const char *oid, *s;
+  ksba_name_t name, name2;
+  unsigned int reason;
+
+  sexp = ksba_cert_get_serial (cert);
+  fputs ("Serial number: ", fp);
+  gpgsm_print_serial (fp, sexp);
+  ksba_free (sexp);
+  putc ('\n', fp);
+
+  dn = ksba_cert_get_issuer (cert, 0);
+  fputs ("       Issuer: ", fp);
+  print_name_raw (fp, dn);
+  ksba_free (dn);
+  putc ('\n', fp);
+  for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++)
+    {
+      fputs ("          aka: ", fp);
+      print_name_raw (fp, dn);
+      ksba_free (dn);
+      putc ('\n', fp);
+    }
+
+  dn = ksba_cert_get_subject (cert, 0);
+  fputs ("      Subject: ", fp);
+  print_name_raw (fp, dn);
+  ksba_free (dn);
+  putc ('\n', fp);
+  for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++)
+    {
+      fputs ("          aka: ", fp);
+      print_name_raw (fp, dn);
+      ksba_free (dn);
+      putc ('\n', fp);
+    }
+
+  dn = gpgsm_get_fingerprint_string (cert, 0);
+  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");
+  xfree (dn);
+
+  dn = gpgsm_get_certid (cert);
+  fprintf (fp, "       certid: %s\n", dn?dn:"error");
+  xfree (dn);
+
+  dn = gpgsm_get_keygrip_hexstring (cert);
+  fprintf (fp, "      keygrip: %s\n", dn?dn:"error");
+  xfree (dn);
+
+  ksba_cert_get_validity (cert, 0, t);
+  fputs ("    notBefore: ", fp);
+  gpgsm_print_time (fp, t);
+  putc ('\n', fp);
+  fputs ("     notAfter: ", fp);
+  ksba_cert_get_validity (cert, 1, t);
+  gpgsm_print_time (fp, t);
+  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?")":"");
+
+  {
+    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:"?");
+  }
+
+  /* subjectKeyIdentifier */
+  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)
+        fputs ("[none]\n", fp);
+      else
+        {
+          gpgsm_print_serial (fp, keyid);
+          ksba_free (keyid);
+          putc ('\n', fp);
+        }
+    }
+  else
+    fputs ("[?]\n", fp);
+
+
+  /* authorityKeyIdentifier */
+  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);
+      else
+        {
+          gpgsm_print_serial (fp, sexp);
+          ksba_free (sexp);
+          putc ('\n', fp);
+          print_names_raw (fp, -15, name);
+          ksba_name_release (name);
+        }
+      if (keyid)
+        {
+          fputs (" authKeyId.ki: ", fp);
+          gpgsm_print_serial (fp, keyid);
+          ksba_free (keyid);
+          putc ('\n', fp);
+        }
+    }
+  else
+    fputs ("[?]\n", fp);
+
+  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));
+      else
+        {
+          if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE))
+            fputs (" digitalSignature", fp);
+          if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION))  
+            fputs (" nonRepudiation", fp);
+          if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) 
+            fputs (" keyEncipherment", fp);
+          if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT))
+            fputs (" dataEncipherment", fp);
+          if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT))    
+            fputs (" keyAgreement", fp);
+          if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN))
+            fputs (" certSign", fp);
+          if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN))  
+            fputs (" crlSign", fp);
+          if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY))
+            fputs (" encipherOnly", fp);
+          if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY))  
+            fputs (" decipherOnly", fp);
+        }
+      putc ('\n', fp);
+    }
+  else
+    fputs ("[none]\n", fp);
+
+  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));
+      else
+        {
+          p = string;
+          while (p && (pend=strchr (p, ':')))
+            {
+              *pend++ = 0;
+              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);
+              p = pend;
+              if (*p != 'C')
+                fputs (" (suggested)", fp);
+              if ((p = strchr (p, '\n')))
+                {
+                  p++;
+                  fputs ("\n               ", fp);
+                }
+            }
+          xfree (string);
+        }
+      putc ('\n', fp);
+    }
+  else
+    fputs ("[none]\n", fp);
+
+
+  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));
+      else
+        {
+          p = string;
+          while (p && (pend=strchr (p, ':')))
+            {
+              *pend++ = 0;
+              for (i=0; key_purpose_map[i].oid; i++)
+                if ( !strcmp (key_purpose_map[i].oid, p) )
+                  break;
+              fputs (p, fp);
+              p = pend;
+              if (*p == 'C')
+                fputs (" (critical)", fp);
+              if ((p = strchr (p, '\n')))
+                {
+                  p++;
+                  fputs ("\n               ", fp);
+                }
+            }
+          xfree (string);
+        }
+      putc ('\n', fp);
+    }
+  else
+    fputs ("[none]\n", fp);
+
+  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));
+      else if (chainlen == -1)
+        fputs ("unlimited", fp);
+      else
+        fprintf (fp, "%d", chainlen);
+      putc ('\n', fp);
+    }
+  else
+    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);
+      print_names_raw (fp, 15, name);
+      if (reason)
+        {
+          fputs ("               reason: ", fp);
+          if ( (reason & KSBA_CRLREASON_UNSPECIFIED))
+            fputs (" unused", stdout);
+          if ( (reason & KSBA_CRLREASON_KEY_COMPROMISE))
+            fputs (" keyCompromise", stdout);
+          if ( (reason & KSBA_CRLREASON_CA_COMPROMISE))
+            fputs (" caCompromise", stdout);
+          if ( (reason & KSBA_CRLREASON_AFFILIATION_CHANGED))
+            fputs (" affiliationChanged", stdout);
+          if ( (reason & KSBA_CRLREASON_SUPERSEDED))
+            fputs (" superseded", stdout);
+          if ( (reason & KSBA_CRLREASON_CESSATION_OF_OPERATION))
+            fputs (" cessationOfOperation", stdout);
+          if ( (reason & KSBA_CRLREASON_CERTIFICATE_HOLD))
+            fputs (" certificateHold", stdout);
+          putchar ('\n');
+        }
+      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);
+  else if (!idx)
+    fputs ("        crlDP: [none]\n", fp);
+
+
+  /* authorityInfoAccess. */
+  for (idx=0; !(err=ksba_cert_get_authority_info_access (cert, idx, &string,
+                                                         &name)); idx++)
+    {
+      fputs ("     authInfo: ", fp);
+      s = get_oid_desc (string, NULL);
+      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);
+  else if (!idx)
+    fputs ("     authInfo: [none]\n", fp);
+
+  /* subjectInfoAccess. */
+  for (idx=0; !(err=ksba_cert_get_subject_info_access (cert, idx, &string,
+                                                         &name)); idx++)
+    {
+      fputs ("  subjectInfo: ", fp);
+      s = get_oid_desc (string, NULL);
+      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);
+  else if (!idx)
+    fputs ("     subjInfo: [none]\n", fp);
+
+
+  for (idx=0; !(err=ksba_cert_get_extension (cert, idx,
+                                             &oid, &i, &off, &len));idx++)
+    {
+      unsigned int flag;
+
+      s = get_oid_desc (oid, &flag);
+
+      if (!(flag & 1))
+        fprintf (fp, "     %s: %s%s%s%s  [%d octets]\n",
+                 i? "critExtn":"    extn",
+                 oid, s?" (":"", s?s:"", s?")":"", (int)len);
+    }
+
+
+  if (with_validation)
+    {
+      err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0);
+      if (!err)
+        fprintf (fp, "  [certificate is good]\n");
+      else
+        fprintf (fp, "  [certificate is bad: %s]\n", gpg_strerror (err));
+    }
+
+  if (opt.with_ephemeral_keys && hd)
+    {
+      unsigned int blobflags;
+
+      err = keydb_get_flags (hd, KEYBOX_FLAG_BLOB, 0, &blobflags);
+      if (err)
+        fprintf (fp, "  [error getting keyflags: %s]\n", gpg_strerror (err));
+      else if ((blobflags & 2))
+        fprintf (fp, "  [stored as ephemeral]\n");
+    }
+
+}
+
+
+
+
 /* List one certificate in standard mode */
 static void
 list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
@@ -359,6 +957,16 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
   gpgsm_print_time (fp, t);
   putc ('\n', fp);
 
+
+  {
+    const char *algoname;
+    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:"?");
+  }
+
+
   err = ksba_cert_get_key_usage (cert, &kusage);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA)
     {
@@ -462,9 +1070,28 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
   fprintf (fp, "  fingerprint: %s\n", dn?dn:"error");
   xfree (dn);
 
+
+
   if (with_validation)
     {
-      err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp);
+      gpg_error_t tmperr;
+      size_t buflen;
+      char buffer[1];
+      
+      err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0);
+      tmperr = ksba_cert_get_user_data (cert, "is_qualified", 
+                                        &buffer, sizeof (buffer), &buflen);
+      if (!tmperr && buflen)
+        {
+          if (*buffer)
+            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");
       else
@@ -475,17 +1102,25 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
 
 /* Same as standard mode mode list all certifying certs too. */
 static void
-list_cert_chain (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int with_validation)
+list_cert_chain (ctrl_t ctrl, KEYDB_HANDLE hd,
+                 ksba_cert_t cert, int raw_mode,
+                 FILE *fp, int with_validation)
 {
   ksba_cert_t next = NULL;
 
-  list_cert_std (ctrl, cert, fp, 0, with_validation);
+  if (raw_mode)
+    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))
     {
       ksba_cert_release (cert);
       fputs ("Certified by\n", fp);
-      list_cert_std (ctrl, next, fp, 0, with_validation);
+      if (raw_mode)
+        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);
@@ -494,17 +1129,21 @@ list_cert_chain (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int with_validation)
 
 
 \f
-/* List all internal keys or just the key given as NAMES.
+/* 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 instead of the standard beautified one.
  */
-static void
-list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode)
+static gpg_error_t
+list_internal_keys (ctrl_t ctrl, strlist_t names, FILE *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;
-  int rc=0;
+  gpg_error_t rc = 0;
   const char *lastresname, *resname;
   int have_secret;
 
@@ -512,6 +1151,7 @@ list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode)
   if (!hd)
     {
       log_error ("keydb_new failed\n");
+      rc = gpg_error (GPG_ERR_GENERAL);
       goto leave;
     }
 
@@ -526,6 +1166,7 @@ list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode)
   desc = xtrycalloc (ndesc, sizeof *desc);
   if (!ndesc)
     {
+      rc = gpg_error_from_syserror ();
       log_error ("out of core\n");
       goto leave;
     }
@@ -549,6 +1190,9 @@ list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode)
       
     }
 
+  if (opt.with_ephemeral_keys)
+    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
@@ -587,7 +1231,7 @@ list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode)
             {
               fprintf (fp, "%s\n", resname );
               for (i=strlen(resname); i; i-- )
-                putchar('-');
+                putc ('-', fp);
               putc ('\n', fp);
               lastresname = resname;
             }
@@ -599,8 +1243,12 @@ list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode)
           char *p = gpgsm_get_keygrip_hexstring (cert);
           if (p)
             {
-              if (!gpgsm_agent_havekey (p))
+              rc = gpgsm_agent_havekey (ctrl, p); 
+             if (!rc)
                 have_secret = 1;
+              else if ( gpg_err_code (rc) != GPG_ERR_NO_SECKEY)
+                goto leave;
+              rc = 0;
               xfree (p);
             }
         }
@@ -612,24 +1260,32 @@ list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode)
           if (ctrl->with_colons)
             list_cert_colon (ctrl, cert, validity, fp, have_secret);
           else if (ctrl->with_chain)
-            list_cert_chain (ctrl, cert, fp, ctrl->with_validation);
+            list_cert_chain (ctrl, hd, cert,
+                             raw_mode, fp, ctrl->with_validation);
           else
             {
-              list_cert_std (ctrl, cert, fp, have_secret,
-                             ctrl->with_validation);
+              if (raw_mode)
+                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);
             }
         }
       ksba_cert_release (cert); 
       cert = NULL;
     }
-  if (rc && rc != -1)
+  if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1 )
+    rc = 0;
+  if (rc)
     log_error ("keydb_search failed: %s\n", gpg_strerror (rc));
   
  leave:
   ksba_cert_release (cert);
   xfree (desc);
   keydb_release (hd);
+  return rc;
 }
 
 
@@ -657,10 +1313,13 @@ list_external_cb (void *cb_value, ksba_cert_t cert)
   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->fp, 0);
+    list_cert_chain (parm->ctrl, NULL, cert, parm->raw_mode, parm->fp, 0);
   else
     {
-      list_cert_std (parm->ctrl, cert, parm->fp, 0, 0);
+      if (parm->raw_mode)
+        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);
     }
 }
@@ -669,8 +1328,8 @@ list_external_cb (void *cb_value, ksba_cert_t cert)
 /* List external keys similar to internal one.  Note: mode does not
    make sense here because it would be unwise to list external secret
    keys */
-static void
-list_external_keys (CTRL ctrl, STRLIST names, FILE *fp)
+static gpg_error_t
+list_external_keys (ctrl_t ctrl, strlist_t names, FILE *fp, int raw_mode)
 {
   int rc;
   struct list_external_parm_s parm;
@@ -680,10 +1339,12 @@ list_external_keys (CTRL ctrl, STRLIST names, FILE *fp)
   parm.print_header = ctrl->no_server;
   parm.with_colons = ctrl->with_colons;
   parm.with_chain = ctrl->with_chain;
+  parm.raw_mode  = raw_mode;
 
   rc = gpgsm_dirmngr_lookup (ctrl, names, list_external_cb, &parm);
   if (rc)
     log_error ("listing external keys failed: %s\n", gpg_strerror (rc));
+  return rc;
 }
 
 /* List all keys or just the key given as NAMES.
@@ -695,12 +1356,16 @@ list_external_keys (CTRL ctrl, STRLIST names, FILE *fp)
       3 = list secret and public keys
     Bit 6: list internal keys
     Bit 7: list external keys
+    Bit 8: Do a raw format dump.
  */
-void
-gpgsm_list_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode)
+gpg_error_t
+gpgsm_list_keys (ctrl_t ctrl, strlist_t names, FILE *fp, unsigned int mode)
 {
+  gpg_error_t err = 0;
+
   if ((mode & (1<<6)))
-      list_internal_keys (ctrl, names, fp, (mode & 3));
-  if ((mode & (1<<7)))
-      list_external_keys (ctrl, names, fp); 
+    err = list_internal_keys (ctrl, names, fp, (mode & 3), (mode&256));
+  if (!err && (mode & (1<<7)))
+    err = list_external_keys (ctrl, names, fp, (mode&256)); 
+  return err;
 }