Add new option --with-keygrip
[gnupg.git] / sm / keylist.c
index 604f9d9..4f876ff 100644 (file)
@@ -1,6 +1,6 @@
 /* keylist.c - Print certificates in various formats.
  * Copyright (C) 1998, 1999, 2000, 2001, 2003,
- *               2004, 2005 Free Software Foundation, Inc.
+ *               2004, 2005, 2008, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -35,6 +35,7 @@
 #include "keydb.h"
 #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
 #include "i18n.h"
+#include "tlv.h"
 
 struct list_external_parm_s 
 {
@@ -77,12 +78,18 @@ struct
 };
 
 
+/* 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 
 {
   char *oid;
   char *name;
-  unsigned int flag;
+  unsigned int flag; /* A flag as described above.  */
 } oidtranstbl[] = {
 
   /* Algorithms. */
@@ -115,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" },
@@ -135,12 +146,12 @@ static struct
   { "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", },
+  { "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" },
@@ -150,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" },
 
@@ -218,6 +229,9 @@ print_key_data (ksba_cert_t cert, estream_t fp)
       putchar(':');
       putchar('\n');
     }
+#else
+  (void)cert;
+  (void)fp;
 #endif
 }
 
@@ -272,6 +286,8 @@ print_capabilities (ksba_cert_t cert, estream_t fp)
     es_putc ('S', fp);
   if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN))
     es_putc ('C', fp);
+
+  es_putc (':', fp);
 }
 
 
@@ -286,7 +302,7 @@ print_time (gnupg_isotime_t t, estream_t fp)
 
 
 /* Return an allocated string with the email address extracted from a
-   DN */
+   DN.  Note hat we use this code also in ../kbx/keybox-blob.c.  */
 static char *
 email_kludge (const char *name)
 {
@@ -311,7 +327,7 @@ email_kludge (const char *name)
 
   /* 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 */
+     OpenSSL generated keys get a nicer and usable listing.  */
   for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++)
     ;
   if (!n)
@@ -361,7 +377,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
   {
     ksba_cert_t next;
 
-    rc = gpgsm_walk_cert_chain (cert, &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);
@@ -404,6 +420,8 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
         *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
@@ -413,7 +431,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
     {
       struct rootca_flags_s dummy_flags;
 
-      rc = gpgsm_agent_istrusted (ctrl, cert, &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)
@@ -465,7 +483,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 */
@@ -491,8 +526,8 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
   kludge_uid = NULL;
   for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++)
     {
-      /* In the case that the same email address is in the subecj DN
-         as weel as in an alternate subject name we avoid printing it
+      /* 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;
@@ -561,6 +596,59 @@ print_names_raw (estream_t fp, int indent, ksba_name_t name)
 }
 
 
+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 no beautification and only minimal
    output sanitation.  It is mainly useful for debugging. */
@@ -581,9 +669,12 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
   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));
+              gpgsm_get_short_fingerprint (cert, NULL));
 
   sexp = ksba_cert_get_serial (cert);
   es_fputs ("          S/N: ", fp);
@@ -892,11 +983,19 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
       unsigned int flag;
 
       s = get_oid_desc (oid, &flag);
+      if ((flag & OID_FLAG_SKIP))
+        continue;
 
-      if (!(flag & 1))
-        es_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);
+        }
     }
 
 
@@ -909,7 +1008,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;
 
@@ -938,9 +1037,13 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t 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));
+              gpgsm_get_short_fingerprint (cert, NULL));
 
   sexp = ksba_cert_get_serial (cert);
   es_fputs ("          S/N: ", fp);
@@ -1053,6 +1156,21 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret,
       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)
     {
@@ -1098,7 +1216,26 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret,
   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)
     {
@@ -1141,7 +1278,7 @@ list_cert_chain (ctrl_t ctrl, KEYDB_HANDLE hd,
   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);
       es_fputs ("Certified by\n", fp);
@@ -1171,9 +1308,11 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
   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)
@@ -1205,7 +1344,7 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t 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",
@@ -1218,7 +1357,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
@@ -1228,6 +1384,7 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t 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)))
     {
@@ -1248,7 +1405,16 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t 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 ) 
@@ -1272,7 +1438,7 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
           if (p)
             {
               rc = gpgsm_agent_havekey (ctrl, p); 
-             if (!rc)
+              if (!rc)
                 have_secret = 1;
               else if ( gpg_err_code (rc) != GPG_ERR_NO_SECKEY)
                 goto leave;
@@ -1301,7 +1467,9 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t 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 )
@@ -1311,6 +1479,7 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
   
  leave:
   ksba_cert_release (cert);
+  ksba_cert_release (lastcert); 
   xfree (desc);
   keydb_release (hd);
   return rc;
@@ -1369,7 +1538,10 @@ list_external_keys (ctrl_t ctrl, strlist_t names, estream_t 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;