core: Change the way TOFU information are represented.
authorWerner Koch <wk@gnupg.org>
Tue, 23 Aug 2016 13:22:28 +0000 (15:22 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 23 Aug 2016 13:24:10 +0000 (15:24 +0200)
* src/gpgme.h.in (struct _gpgme_signature): Remove field 'tofu'.  Add
field 'key'.
(struct _gpgme_key): Add field 'fpr'.
(struct _gpgme_user_id): Add field 'tofu'.
(struct _gpgme_tofu_info): Remove fields 'address' and 'fpr'.
* src/key.c (gpgme_key_unref): Release TOFU and FPR.
* src/keylist.c (keylist_colon_handler): Store the fingerprint of the
first subkey also in KEY.
* src/verify.c (release_tofu_info): Remove.
(release_op_data): Release KEY.
(parse_tofu_user): Rewrite for new data structure.
(parse_tofu_stats): Ditto.
(parse_tofu_stats_long): Ditto.
* tests/run-verify.c (print_result): Ditto.
* tests/run-keylist.c (main): Print more fields.
--

TOFU information are now associated with the user ID and not with a
separate object.

Note that this breaks code relying on the former non-released TOFU
feature.  The C++ bindings won't work right now.

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

diff --git a/NEWS b/NEWS
index ce16687..1294e0b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -15,8 +15,10 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
  GPGME_PK_EDDSA                 NEW.
  gpgme_set_ctx_flag             NEW.
  gpgme_data_set_flag            NEW.
- gpgme_signature_t              EXTENDED: New field tofu.
+ gpgme_signature_t              EXTENDED: New field key.
+ gpgme_key_t                    EXTENDED: New field fpr.
  gpgme_subkey_t                 EXTENDED: New field keygrip.
+ gpgme_user_id_t                EXTENDED: New field tofu.
  gpgme_tofu_policy_t            NEW.
  gpgme_tofu_info_t              NEW.
  GPGME_STATUS_KEY_CONSIDERED    NEW.
index b28c6ca..02551d9 100644 (file)
@@ -3017,6 +3017,10 @@ This is the key ID of the subkey in hexadecimal digits.
 This is the fingerprint of the subkey in hexadecimal digits, if
 available.
 
+@item char *keygrip
+The keygrip of the subkey in hex digit form or @code{NULL} if not
+availabale.
+
 @item long int timestamp
 This is the creation timestamp of the subkey.  This is -1 if the
 timestamp is invalid, and 0 if it is not available.
@@ -3144,6 +3148,16 @@ This is the comment component of @code{uid}, if available.
 @item char *email
 This is the email component of @code{uid}, if available.
 
+@item char *address;
+The mail address (addr-spec from RFC-5322) of the user ID string.
+This is general the same as the @code{email} part of this structure
+but might be slightly different.  If no mail address is available
+@code{NULL} is stored.
+
+@item gpgme_tofu_info_t tofu
+If not @code{NULL} information from the TOFU database pertaining to
+this user id.
+
 @item gpgme_key_sig_t signatures
 This is a linked list with the signatures on this user ID.
 @end table
@@ -3168,8 +3182,8 @@ This is true if the key is disabled.
 
 @item unsigned int invalid : 1
 This is true if the key is invalid. This might have several reasons,
-for a example for the S/MIME backend, it will be set in during key
-listsing if the key could not be validated due to a missing
+for a example for the S/MIME backend, it will be set during key
+listings if the key could not be validated due to missing
 certificates or unmatched policies.
 
 @item unsigned int can_encrypt : 1
@@ -3224,6 +3238,13 @@ in the list is the primary key and usually available.
 @item gpgme_user_id_t uids
 This is a linked list with the user IDs of the key.  The first user ID
 in the list is the main (or primary) user ID.
+
+@item char *fpr
+This field gives the fingerprint of the primary key.  Note that
+this is a copy of the fingerprint of the first subkey.  For an
+incomplete key (for example from a verification result) a subkey may
+be missing but this field may be set nevertheless.
+
 @end table
 @end deftp
 
@@ -4870,6 +4891,13 @@ The hash algorithm used to create this signature.
 
 @item char *pka_address
 The mailbox from the PKA information or @code{NULL}.
+
+@item gpgme_key_t key
+An object describing the key used to create the signature.  This key
+object may be incomplete in that it only conveys information
+availabale directly with a signature.  It may also be @code{NULL} if
+such information is not readily available.
+
 @end table
 @end deftp
 
index 49cea77..c07cac8 100644 (file)
@@ -624,6 +624,41 @@ struct _gpgme_engine_info
 typedef struct _gpgme_engine_info *gpgme_engine_info_t;
 
 
+/* An object with TOFU information.  */
+struct _gpgme_tofu_info
+{
+  struct _gpgme_tofu_info *next;
+
+  /* The TOFU validity:
+   *  0 := conflict
+   *  1 := key without history
+   *  2 := key with too little history
+   *  3 := key with enough history for basic trust
+   *  4 := key with a lot of history
+   */
+  unsigned int validity : 3;
+
+  /* The TOFU policy (gpgme_tofu_policy_t).  */
+  unsigned int policy : 4;
+
+  unsigned int _rfu : 25;
+
+  /* Number of signatures seen for this binding.  Capped at USHRT_MAX.  */
+  unsigned short signcount;
+  /* Number of encryptions done with this binding.  Capped at USHRT_MAX.  */
+  unsigned short encrcount;
+
+  /* Number of seconds since the first and the most recently seen
+   * message was verified.  */
+  unsigned int firstseen;
+  unsigned int lastseen;
+
+  /* If non-NULL a human readable string summarizing the TOFU data. */
+  char *description;
+};
+typedef struct _gpgme_tofu_info *gpgme_tofu_info_t;
+
+
 /* A subkey from a key.  */
 struct _gpgme_subkey
 {
@@ -807,6 +842,9 @@ struct _gpgme_user_id
    * might be slightly different.  IF no mail address is available
    * NULL is stored.  */
   char *address;
+
+  /* The malloced tofo information or NULL.  */
+  gpgme_tofu_info_t tofu;
 };
 typedef struct _gpgme_user_id *gpgme_user_id_t;
 
@@ -883,6 +921,11 @@ struct _gpgme_key
 
   /* The keylist mode that was active when listing the key.  */
   gpgme_keylist_mode_t keylist_mode;
+
+  /* This field gives the fingerprint of the primary key.  Note that
+   * this is a copy of the FPR of the first subkey.  We need it here
+   * to allow for an incomplete key object.  */
+  char *fpr;
 };
 typedef struct _gpgme_key *gpgme_key_t;
 
@@ -1570,50 +1613,6 @@ typedef enum
 gpgme_sigsum_t;
 
 
-struct _gpgme_tofu_info
-{
-  struct _gpgme_tofu_info *next;
-
-  /* The mail address (addr-spec from RFC5322) of the tofu binding.
-   *
-   * If no mail address is set for a User ID this is the name used
-   * for the user ID. Can be ambiguous when the same mail address or
-   * name is used in multiple user ids.
-   */
-  char *address;
-
-  /* The fingerprint of the primary key.  */
-  char *fpr;
-
-  /* The TOFU validity:
-   *  0 := conflict
-   *  1 := key without history
-   *  2 := key with too little history
-   *  3 := key with enough history for basic trust
-   *  4 := key with a lot of history
-   */
-  unsigned int validity : 3;
-
-  /* The TOFU policy (gpgme_tofu_policy_t).  */
-  unsigned int policy : 4;
-
-  unsigned int _rfu : 25;
-
-  /* Number of signatures seen for this binding.  Capped at USHRT_MAX.  */
-  unsigned short signcount;
-  unsigned short reserved;
-
-  /* Number of seconds since the first and the most recently seen
-   * message was verified.  */
-  unsigned int firstseen;
-  unsigned int lastseen;
-
-  /* If non-NULL a human readable string summarizing the TOFU data. */
-  char *description;
-};
-typedef struct _gpgme_tofu_info *gpgme_tofu_info_t;
-
-
 struct _gpgme_signature
 {
   struct _gpgme_signature *next;
@@ -1621,7 +1620,7 @@ struct _gpgme_signature
   /* A summary of the signature status.  */
   gpgme_sigsum_t summary;
 
-  /* The fingerprint or key ID of the signature.  */
+  /* The fingerprint of the signature.  This can be a subkey.  */
   char *fpr;
 
   /* The status of the signature.  */
@@ -1660,8 +1659,9 @@ struct _gpgme_signature
   /* The mailbox from the PKA information or NULL. */
   char *pka_address;
 
-  /* If non-NULL, TOFU info for this signature are available.  */
-  gpgme_tofu_info_t tofu;
+  /* If non-NULL, a possible incomplete key object with the data
+   * available for the signature.  */
+  gpgme_key_t key;
 };
 typedef struct _gpgme_signature *gpgme_signature_t;
 
index f642501..38acc71 100644 (file)
--- a/src/key.c
+++ b/src/key.c
@@ -356,6 +356,7 @@ gpgme_key_unref (gpgme_key_t key)
     {
       gpgme_user_id_t next_uid = uid->next;
       gpgme_key_sig_t keysig = uid->signatures;
+      gpgme_tofu_info_t tofu = uid->tofu;
 
       while (keysig)
        {
@@ -373,8 +374,21 @@ gpgme_key_unref (gpgme_key_t key)
           free (keysig);
          keysig = next_keysig;
         }
+
+      while (tofu)
+        {
+          /* NB: The ->next is currently not used but we are prepared
+           * for it.  */
+          gpgme_tofu_info_t tofu_next = tofu->next;
+
+          free (tofu->description);
+          free (tofu);
+          tofu = tofu_next;
+        }
+
       if (uid->address && uid->address != uid->email)
         free (uid->address);
+
       free (uid);
       uid = next_uid;
     }
@@ -386,10 +400,13 @@ gpgme_key_unref (gpgme_key_t key)
 
   if (key->chain_id)
     free (key->chain_id);
+  if (key->fpr)
+    free (key->fpr);
 
   free (key);
 }
 
+
 \f
 /* Support functions.  */
 
index 5a346ea..38ddd0c 100644 (file)
@@ -708,6 +708,22 @@ keylist_colon_handler (void *priv, char *line)
               if (!subkey->fpr)
                 return gpg_error_from_syserror ();
             }
+          /* If this is the first subkey, store the fingerprint also
+             in the KEY object.  */
+          if (subkey == key->subkeys)
+            {
+              if (key->fpr && strcmp (key->fpr, subkey->fpr))
+                {
+                  /* FPR already set but mismatch: Should never happen.  */
+                  return trace_gpg_error (GPG_ERR_INTERNAL);
+                }
+              if (!key->fpr)
+                {
+                  key->fpr = strdup (subkey->fpr);
+                  if (!key->fpr)
+                    return gpg_error_from_syserror ();
+                }
+            }
        }
 
       /* Field 13 has the gpgsm chain ID (take only the first one).  */
index 9c27529..97b1019 100644 (file)
--- a/src/ops.h
+++ b/src/ops.h
@@ -138,9 +138,11 @@ gpgme_error_t _gpgme_progress_status_handler (void *priv,
 gpgme_error_t _gpgme_key_new (gpgme_key_t *r_key);
 gpgme_error_t _gpgme_key_add_subkey (gpgme_key_t key,
                                     gpgme_subkey_t *r_subkey);
-gpgme_error_t _gpgme_key_append_name (gpgme_key_t key, const char *src, int convert);
+gpgme_error_t _gpgme_key_append_name (gpgme_key_t key,
+                                      const char *src, int convert);
 gpgme_key_sig_t _gpgme_key_add_sig (gpgme_key_t key, char *src);
 
+
 \f
 /* From keylist.c.  */
 void _gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type,
index 1ec09fe..173d1cb 100644 (file)
@@ -50,22 +50,6 @@ typedef struct
 
 
 static void
-release_tofu_info (gpgme_tofu_info_t t)
-{
-  while (t)
-    {
-      gpgme_tofu_info_t t2 = t->next;
-
-      free (t->address);
-      free (t->fpr);
-      free (t->description);
-      free (t);
-      t = t2;
-    }
-}
-
-
-static void
 release_op_data (void *hook)
 {
   op_data_t opd = (op_data_t) hook;
@@ -88,7 +72,8 @@ release_op_data (void *hook)
        free (sig->fpr);
       if (sig->pka_address)
        free (sig->pka_address);
-      release_tofu_info (sig->tofu);
+      if (sig->key)
+        gpgme_key_unref (sig->key);
       free (sig);
       sig = next;
     }
@@ -690,49 +675,80 @@ parse_tofu_user (gpgme_signature_t sig, char *args)
 {
   gpg_error_t err;
   char *tail;
-  gpgme_tofu_info_t ti, ti2;
+  gpgme_user_id_t uid;
+  gpgme_tofu_info_t ti;
+  char *fpr = NULL;
+  char *address = NULL;
 
   tail = strchr (args, ' ');
   if (!tail || tail == args)
-    return trace_gpg_error (GPG_ERR_INV_ENGINE);  /* No fingerprint.  */
+    {
+      err = trace_gpg_error (GPG_ERR_INV_ENGINE);  /* No fingerprint.  */
+      goto leave;
+    }
   *tail++ = 0;
 
-  ti = calloc (1, sizeof *ti);
-  if (!ti)
-    return gpg_error_from_syserror ();
-
-  ti->fpr = strdup (args);
-  if (!ti->fpr)
+  fpr = strdup (args);
+  if (!fpr)
     {
-      free (ti);
-      return gpg_error_from_syserror ();
+      err = gpg_error_from_syserror ();
+      goto leave;
     }
 
   args = tail;
   tail = strchr (args, ' ');
   if (tail == args)
-    return trace_gpg_error (GPG_ERR_INV_ENGINE);  /* No addr-spec.  */
+    {
+      err = trace_gpg_error (GPG_ERR_INV_ENGINE);  /* No addr-spec.  */
+      goto leave;
+    }
   if (tail)
     *tail = 0;
 
-  err = _gpgme_decode_percent_string (args, &ti->address, 0, 0);
+  err = _gpgme_decode_percent_string (args, &address, 0, 0);
   if (err)
+    goto leave;
+
+  if (!sig->key)
     {
-      free (ti);
-      return err;
+      err = _gpgme_key_new (&sig->key);
+      if (err)
+        goto leave;
+      sig->key->fpr = fpr;
+      fpr = NULL;
+    }
+  else if (!sig->key->fpr)
+    {
+      err = trace_gpg_error (GPG_ERR_INTERNAL);
+      goto leave;
+    }
+  else if (strcmp (sig->key->fpr, fpr))
+    {
+      /* The engine did not emit NEWSIG before a new key.  */
+      err = trace_gpg_error (GPG_ERR_INV_ENGINE);
+      goto leave;
     }
 
-  /* Append to the tofu info list.  */
-  if (!sig->tofu)
-    sig->tofu = ti;
-  else
+  err = _gpgme_key_append_name (sig->key, address, 0);
+  if (err)
+    goto leave;
+
+  uid = sig->key->_last_uid;
+  assert (uid);
+
+  ti = calloc (1, sizeof *ti);
+  if (!ti)
     {
-      for (ti2 = sig->tofu; ti2->next; ti2 = ti2->next)
-        ;
-      ti2->next = ti;
+      err = gpg_error_from_syserror ();
+      goto leave;
     }
+  uid->tofu = ti;
 
-  return 0;
+
+ leave:
+  free (fpr);
+  free (address);
+  return err;
 }
 
 
@@ -749,12 +765,10 @@ parse_tofu_stats (gpgme_signature_t sig, char *args)
   int nfields;
   unsigned long uval;
 
-  if (!sig->tofu)
+  if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu))
     return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen.  */
-  for (ti = sig->tofu; ti->next; ti = ti->next)
-    ;
   if (ti->firstseen || ti->signcount || ti->validity || ti->policy)
-    return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already seen.  */
+    return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set.  */
 
   nfields = _gpgme_split_fields (args, field, DIM (field));
   if (nfields < 3)
@@ -825,12 +839,10 @@ parse_tofu_stats_long (gpgme_signature_t sig, char *args, int raw)
   gpgme_tofu_info_t ti;
   char *p;
 
-  if (!sig->tofu)
+  if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu))
     return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen.  */
-  for (ti = sig->tofu; ti->next; ti = ti->next)
-    ;
   if (ti->description)
-    return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already seen.  */
+    return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set.  */
 
   err = _gpgme_decode_percent_string (args, &ti->description, 0, 0);
   if (err)
index cc4c354..bae2dbb 100644 (file)
@@ -233,7 +233,14 @@ 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 ("valid  %d: %s\n", nuids,
+          printf ("  mbox %d: %s\n", nuids, nonnull(uid->address));
+          if (uid->email && uid->email != uid->address)
+            printf (" email %d: %s\n", nuids, uid->email);
+          if (uid->name)
+            printf ("  name %d: %s\n", nuids, uid->name);
+          if (uid->comment)
+            printf (" cmmnt %d: %s\n", nuids, uid->comment);
+          printf (" valid %d: %s\n", nuids,
                   uid->validity == GPGME_VALIDITY_UNKNOWN? "unknown":
                   uid->validity == GPGME_VALIDITY_UNDEFINED? "undefined":
                   uid->validity == GPGME_VALIDITY_NEVER? "never":
index b174516..ef4dd32 100644 (file)
@@ -111,6 +111,7 @@ print_result (gpgme_verify_result_t result)
 {
   gpgme_signature_t sig;
   gpgme_sig_notation_t nt;
+  gpgme_user_id_t uid;
   gpgme_tofu_info_t ti;
   int count = 0;
 
@@ -153,29 +154,34 @@ print_result (gpgme_verify_result_t result)
           if ((nt->value?strlen (nt->value):0) != nt->value_len)
             printf ("    warning : value larger (%d)\n", nt->value_len);
         }
-      for (ti = sig->tofu; ti; ti = ti->next)
+      if (sig->key)
         {
-          printf ("  tofu addr .: %s\n", ti->address);
-          if (!sig->fpr || strcmp (sig->fpr, ti->fpr))
-            printf ("    WARNING .: fpr mismatch (%s)\n", ti->fpr);
-          printf ("    validity : %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 ("    sigcount : %hu\n", ti->signcount);
-          printf ("    firstseen: %u\n", ti->firstseen);
-          printf ("    lastseen : %u\n", ti->lastseen);
-          printf ("    desc ....: ");
-          print_description (nonnull (ti->description), 15);
+          printf ("  primary fpr: %s\n", nonnull (sig->key->fpr));
+          for (uid = sig->key->uids; uid; uid = uid->next)
+            {
+              printf ("  tofu addr .: %s\n", nonnull (uid->address));
+              ti = uid->tofu;
+              if (!ti)
+                continue;
+              printf ("    validity : %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 ("    sigcount : %hu\n", ti->signcount);
+              printf ("    firstseen: %u\n", ti->firstseen);
+              printf ("    lastseen : %u\n", ti->lastseen);
+              printf ("    desc ....: ");
+              print_description (nonnull (ti->description), 15);
+            }
         }
     }
 }