core: Add GPGME_KEYLIST_MODE_WITH_TOFU.
authorWerner Koch <wk@gnupg.org>
Thu, 25 Aug 2016 09:38:03 +0000 (11:38 +0200)
committerWerner Koch <wk@gnupg.org>
Thu, 25 Aug 2016 09:38:03 +0000 (11:38 +0200)
* src/gpgme.h.in (GPGME_KEYLIST_MODE_WITH_TOFU): New.
* src/engine-gpg.c (gpg_keylist_build_options): Use that.
* src/keylist.c: Include limits.h.
(parse_tfs_record): New.
(keylist_colon_handler): Support TFS record.
* tests/run-keylist.c: Include time.h.
(isotimestr): New.
(main): Add option --tofu.  Print TOFU info.
* tests/run-verify.c: Include time.h.
(isotimestr): New.
(print_result): Use isotimestr for TOFU dates.

Signed-off-by: Werner Koch <wk@gnupg.org>
NEWS
doc/gpgme.texi
src/engine-gpg.c
src/gpgme.h.in
src/keylist.c
tests/run-keylist.c
tests/run-verify.c

diff --git a/NEWS b/NEWS
index 1294e0b..da331b4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,7 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
  GPGME_STATUS_TOFU_STATS        NEW.
  GPGME_STATUS_TOFU_STATS_LONG   NEW.
  GPGME_STATUS_NOTATION_FLAGS    NEW.
+ GPGME_KEYLIST_MODE_WITH_TOFU   NEW.
  GPGME_DATA_TYPE_PGP_ENCRYPTED  NEW.
  GPGME_DATA_TYPE_PGP_SIGNATURE  NEW.
  GPGME_DATA_ENCODING_MIME       NEW.
index 02551d9..dfc9548 100644 (file)
@@ -2683,6 +2683,11 @@ signature notations on key signatures should be included in the listed
 keys.  This only works if @code{GPGME_KEYLIST_MODE_SIGS} is also
 enabled.
 
+@item GPGME_KEYLIST_MODE_WITH_TOFU
+The @code{GPGME_KEYLIST_MODE_WITH_TOFU} symbol specifies that
+information pertaining to the TOFU trust model should be included in
+the listed keys.
+
 @item GPGME_KEYLIST_MODE_WITH_SECRET
 The @code{GPGME_KEYLIST_MODE_WITH_SECRET} returns information about
 the presence of a corresponding secret key in a public key listing.  A
index 3edac6c..7036ee0 100644 (file)
@@ -2338,8 +2338,13 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
         err = add_arg (gpg, "--with-fingerprint");
     }
 
+  if (!err && (mode & GPGME_KEYLIST_MODE_WITH_TOFU)
+      && have_gpg_version (gpg, "2.1.16"))
+    err = add_arg (gpg, "--with-tofu-info");
+
   if (!err && (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
     err = add_arg (gpg, "--with-secret");
+
   if (!err
       && (mode & GPGME_KEYLIST_MODE_SIGS)
       && (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS))
@@ -2348,6 +2353,7 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
       if (!err)
        err = add_arg (gpg, "show-sig-subpackets=\"20,26\"");
     }
+
   if (!err)
     {
       if ( (mode & GPGME_KEYLIST_MODE_EXTERN) )
@@ -2379,6 +2385,7 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
                             ? "--check-sigs" : "--list-keys"));
         }
     }
+
   if (!err)
     err = add_arg (gpg, "--");
 
index 79a7b9f..57f3446 100644 (file)
@@ -411,6 +411,7 @@ gpgme_protocol_t;
 #define GPGME_KEYLIST_MODE_SIGS                        4
 #define GPGME_KEYLIST_MODE_SIG_NOTATIONS       8
 #define GPGME_KEYLIST_MODE_WITH_SECRET         16
+#define GPGME_KEYLIST_MODE_WITH_TOFU           32
 #define GPGME_KEYLIST_MODE_EPHEMERAL            128
 #define GPGME_KEYLIST_MODE_VALIDATE            256
 
@@ -843,7 +844,7 @@ struct _gpgme_user_id
    * NULL is stored.  */
   char *address;
 
-  /* The malloced tofo information or NULL.  */
+  /* The malloced TOFU information or NULL.  */
   gpgme_tofu_info_t tofu;
 };
 typedef struct _gpgme_user_id *gpgme_user_id_t;
index 38ddd0c..9f1e68d 100644 (file)
@@ -33,6 +33,7 @@
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
+#include <limits.h>
 
 /* Suppress warning for accessing deprecated member "class".  */
 #define _GPGME_IN_GPGME
@@ -403,6 +404,84 @@ parse_sec_field15 (gpgme_key_t key, gpgme_subkey_t subkey, char *field)
 }
 
 
+/* Parse a tfs record.  */
+static gpg_error_t
+parse_tfs_record (gpgme_user_id_t uid, char **field, int nfield)
+{
+  gpg_error_t err;
+  gpgme_tofu_info_t ti;
+  unsigned long uval;
+
+  /* We add only the first TOFU record in case future versions emit
+   * several.  */
+  if (uid->tofu)
+    return 0;
+
+  /* Check that we have enough fields and that the version is supported.  */
+  if (nfield < 8 || atoi(field[1]) != 1)
+    return trace_gpg_error (GPG_ERR_INV_ENGINE);
+
+  ti = calloc (1, sizeof *ti);
+  if (!ti)
+    return gpg_error_from_syserror ();
+
+  /* Note that we allow a value of up to 7 which is what we can store
+   * in the ti->validity.  */
+  err = _gpgme_strtoul_field (field[2], &uval);
+  if (err || uval > 7)
+    goto inv_engine;
+  ti->validity = uval;
+
+  /* Parse the sign-count.  */
+  err = _gpgme_strtoul_field (field[3], &uval);
+  if (err)
+    goto inv_engine;
+  if (uval > USHRT_MAX)
+    uval = USHRT_MAX;
+  ti->signcount = uval;
+
+  /* Parse the encr-count.  */
+  err = _gpgme_strtoul_field (field[4], &uval);
+  if (err)
+    goto inv_engine;
+  if (uval > USHRT_MAX)
+    uval = USHRT_MAX;
+  ti->encrcount = uval;
+
+  /* Parse the policy.  */
+  if (!strcmp (field[5], "none"))
+    ti->policy = GPGME_TOFU_POLICY_NONE;
+  else if (!strcmp (field[5], "auto"))
+    ti->policy = GPGME_TOFU_POLICY_AUTO;
+  else if (!strcmp (field[5], "good"))
+    ti->policy = GPGME_TOFU_POLICY_GOOD;
+  else if (!strcmp (field[5], "bad"))
+    ti->policy = GPGME_TOFU_POLICY_BAD;
+  else if (!strcmp (field[5], "ask"))
+    ti->policy = GPGME_TOFU_POLICY_ASK;
+  else /* "unknown" and invalid policy strings.  */
+    ti->policy = GPGME_TOFU_POLICY_UNKNOWN;
+
+  /* Parse first and last seen timestamps.  */
+  err = _gpgme_strtoul_field (field[6], &uval);
+  if (err)
+    goto inv_engine;
+  ti->firstseen = uval;
+  err = _gpgme_strtoul_field (field[7], &uval);
+  if (err)
+    goto inv_engine;
+  ti->lastseen = uval;
+
+  /* Ready.  */
+  uid->tofu = ti;
+  return 0;
+
+ inv_engine:
+  free (ti);
+  return trace_gpg_error (GPG_ERR_INV_ENGINE);
+}
+
+
 /* We have read an entire key into tmp_key and should now finish it.
    It is assumed that this releases tmp_key.  */
 static void
@@ -426,7 +505,7 @@ keylist_colon_handler (void *priv, char *line)
   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
   enum
     {
-      RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_GRP,
+      RT_NONE, RT_SIG, RT_UID, RT_TFS, RT_SUB, RT_PUB, RT_FPR, RT_GRP,
       RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK
     }
   rectype = RT_NONE;
@@ -483,6 +562,8 @@ keylist_colon_handler (void *priv, char *line)
     rectype = RT_GRP;
   else if (!strcmp (field[0], "uid") && key)
     rectype = RT_UID;
+  else if (!strcmp (field[0], "tfs") && key)
+    rectype = RT_TFS;
   else if (!strcmp (field[0], "sub") && key)
     rectype = RT_SUB;
   else if (!strcmp (field[0], "ssb") && key)
@@ -492,10 +573,10 @@ keylist_colon_handler (void *priv, char *line)
   else
     rectype = RT_NONE;
 
-  /* Only look at signatures immediately following a user ID.  For
-     this, clear the user ID pointer when encountering anything but a
-     signature.  */
-  if (rectype != RT_SIG && rectype != RT_REV)
+  /* Only look at signature and trust info records immediately
+     following a user ID.  For this, clear the user ID pointer when
+     encountering anything but a signature or trust record.  */
+  if (rectype != RT_SIG && rectype != RT_REV && rectype != RT_TFS)
     opd->tmp_uid = NULL;
 
   /* Only look at subpackets immediately following a signature.  For
@@ -695,6 +776,15 @@ keylist_colon_handler (void *priv, char *line)
        }
       break;
 
+    case RT_TFS:
+      if (opd->tmp_uid)
+       {
+          err = parse_tfs_record (opd->tmp_uid, field, fields);
+          if (err)
+            return err;
+        }
+      break;
+
     case RT_FPR:
       /* Field 10 has the fingerprint (take only the first one).  */
       if (fields >= 10 && field[9] && *field[9])
index bae2dbb..00f874d 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <time.h>
 
 #include <gpgme.h>
 
@@ -49,6 +50,7 @@ show_usage (int ex)
          "  --local          use GPGME_KEYLIST_MODE_LOCAL\n"
          "  --extern         use GPGME_KEYLIST_MODE_EXTERN\n"
          "  --sigs           use GPGME_KEYLIST_MODE_SIGS\n"
+         "  --tofu           use GPGME_KEYLIST_MODE_TOFU\n"
          "  --sig-notations  use GPGME_KEYLIST_MODE_SIG_NOTATIONS\n"
          "  --ephemeral      use GPGME_KEYLIST_MODE_EPHEMERAL\n"
          "  --validate       use GPGME_KEYLIST_MODE_VALIDATE\n"
@@ -60,6 +62,26 @@ show_usage (int ex)
 }
 
 
+static const char *
+isotimestr (unsigned long value)
+{
+  time_t t;
+  static char buffer[25+5];
+  struct tm *tp;
+
+  if (!value)
+    return "none";
+  t = value;
+
+  tp = gmtime (&t);
+  snprintf (buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d",
+            1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+            tp->tm_hour, tp->tm_min, tp->tm_sec);
+  return buffer;
+}
+
+
+
 int
 main (int argc, char **argv)
 {
@@ -120,6 +142,11 @@ main (int argc, char **argv)
           mode |= GPGME_KEYLIST_MODE_EXTERN;
           argc--; argv++;
         }
+      else if (!strcmp (*argv, "--tofu"))
+        {
+          mode |= GPGME_KEYLIST_MODE_WITH_TOFU;
+          argc--; argv++;
+        }
       else if (!strcmp (*argv, "--sigs"))
         {
           mode |= GPGME_KEYLIST_MODE_SIGS;
@@ -181,6 +208,7 @@ main (int argc, char **argv)
   while (!(err = gpgme_op_keylist_next (ctx, &key)))
     {
       gpgme_user_id_t uid;
+      gpgme_tofu_info_t ti;
       int nuids;
       int nsub;
 
@@ -233,24 +261,42 @@ main (int argc, char **argv)
       for (nuids=0, uid=key->uids; uid; uid = uid->next, nuids++)
         {
           printf ("userid %d: %s\n", nuids, nonnull(uid->uid));
-          printf ("  mbox %d: %s\n", nuids, nonnull(uid->address));
+          printf ("    mbox: %s\n", nonnull(uid->address));
           if (uid->email && uid->email != uid->address)
-            printf (" email %d: %s\n", nuids, uid->email);
+            printf ("   email: %s\n", uid->email);
           if (uid->name)
-            printf ("  name %d: %s\n", nuids, uid->name);
+            printf ("    name: %s\n", uid->name);
           if (uid->comment)
-            printf (" cmmnt %d: %s\n", nuids, uid->comment);
-          printf (" valid %d: %s\n", nuids,
+            printf ("   cmmnt: %s\n", uid->comment);
+          printf ("   valid: %s\n",
                   uid->validity == GPGME_VALIDITY_UNKNOWN? "unknown":
                   uid->validity == GPGME_VALIDITY_UNDEFINED? "undefined":
                   uid->validity == GPGME_VALIDITY_NEVER? "never":
                   uid->validity == GPGME_VALIDITY_MARGINAL? "marginal":
                   uid->validity == GPGME_VALIDITY_FULL? "full":
                   uid->validity == GPGME_VALIDITY_ULTIMATE? "ultimate": "[?]");
+          if ((ti = uid->tofu))
+            {
+              printf ("    tofu: %u (%s)\n", ti->validity,
+                      ti->validity == 0? "conflict" :
+                      ti->validity == 1? "no history" :
+                      ti->validity == 2? "little history" :
+                      ti->validity == 3? "enough history" :
+                      ti->validity == 4? "lot of history" : "?");
+              printf ("  policy: %u (%s)\n", ti->policy,
+                      ti->policy == GPGME_TOFU_POLICY_NONE? "none" :
+                      ti->policy == GPGME_TOFU_POLICY_AUTO? "auto" :
+                      ti->policy == GPGME_TOFU_POLICY_GOOD? "good" :
+                      ti->policy == GPGME_TOFU_POLICY_UNKNOWN? "unknown" :
+                      ti->policy == GPGME_TOFU_POLICY_BAD? "bad" :
+                      ti->policy == GPGME_TOFU_POLICY_ASK? "ask" : "?");
+              printf ("   nsigs: %hu\n", ti->signcount);
+              printf ("   nencr: %hu\n", ti->encrcount);
+              printf ("   first: %s\n", isotimestr (ti->firstseen));
+              printf ("    last: %s\n", isotimestr (ti->lastseen));
+            }
         }
 
-
-
       putchar ('\n');
 
       if (import)
index ef4dd32..3c18d3b 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <time.h>
 
 #include <gpgme.h>
 
 
 static int verbose;
 
+
+static const char *
+isotimestr (unsigned long value)
+{
+  time_t t;
+  static char buffer[25+5];
+  struct tm *tp;
+
+  if (!value)
+    return "none";
+  t = value;
+
+  tp = gmtime (&t);
+  snprintf (buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d",
+            1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+            tp->tm_hour, tp->tm_min, tp->tm_sec);
+  return buffer;
+}
+
+
 static gpg_error_t
 status_cb (void *opaque, const char *keyword, const char *value)
 {
@@ -177,8 +198,8 @@ print_result (gpgme_verify_result_t result)
                       ti->policy == GPGME_TOFU_POLICY_BAD? "bad" :
                       ti->policy == GPGME_TOFU_POLICY_ASK? "ask" : "?");
               printf ("    sigcount : %hu\n", ti->signcount);
-              printf ("    firstseen: %u\n", ti->firstseen);
-              printf ("    lastseen : %u\n", ti->lastseen);
+              printf ("    firstseen: %s\n", isotimestr (ti->firstseen));
+              printf ("    lastseen : %s\n", isotimestr (ti->lastseen));
               printf ("    desc ....: ");
               print_description (nonnull (ti->description), 15);
             }