[git] GnuPG - branch, master, updated. gnupg-2.1.16-9-g037f9de

by Neal H. Walfield cvs at cvs.gnupg.org
Mon Nov 21 23:10:24 CET 2016


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, master has been updated
       via  037f9de09298a31026ea2ab5fbd4a599b11cc34f (commit)
       via  182efc5b5d20ac0d43501a22f349a23dc06a27a4 (commit)
       via  7142b293c870d73ce0146bfb90e6a556e0079650 (commit)
       via  91a0483c5db8ee4510981448a705981ee1cce199 (commit)
      from  362afb4f6074aaf5c9a6cacdf6a3530ec3f55d2d (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 037f9de09298a31026ea2ab5fbd4a599b11cc34f
Author: Neal H. Walfield <neal at g10code.com>
Date:   Mon Nov 21 22:47:30 2016 +0100

    g10: Cache the effective policy.  Recompute it when required.
    
    * g10/tofu.c (initdb): Add column effective_policy to the bindings
    table.
    (record_binding): New parameters effective_policy and set_conflict.
    Save the effective policy.  If SET_CONFLICT is set, then set conflict
    according to CONFLICT.  Otherwise, preserve the current value of
    conflict.  Update callers.
    (get_trust): Don't compute the effective policy here...
    (get_policy): ... do it here, if it was not cached.  Take new
    parameters, PK, the public key, and NOW, the time that the operation
    started.  Update callers.
    (show_statistics): New parameter PK.  Pass it to get_policy.  Update
    callers.
    (tofu_notice_key_changed): New function.
    * g10/gpgv.c (tofu_notice_key_changed): New stub.
    * g10/import.c (import_revoke_cert): Take additional argument CTRL.
    Pass it to keydb_update_keyblock.
    * g10/keydb.c (keydb_update_keyblock): Take additional argument CTRL.
    Update callers.
    [USE_TOFU]: Call tofu_notice_key_changed.
    * g10/test-stubs.c (tofu_notice_key_changed): New stub.
    * tests/openpgp/tofu.scm: Assume that manually setting a binding's
    policy to auto does not cause the tofu engine to forget about any
    conflict.
    
    --
    Signed-off-by: Neal H. Walfield <neal at g10code.com>
    
    We now store the computed policy in the tofu DB (in the
    effective_policy column of the bindings table) to avoid computing it
    every time, which is expensive.  Further, policy is never overridden
    in case of a conflict.  Instead, we detect a conflict if CONFLICT is
    not empty.
    
    This change is backwards compatible to existing DBs.  The only minor
    incompatibility is that unresolved conflicts won't be automatically
    resolved in case we import a direct signature, or cross signatures.

diff --git a/g10/gpgv.c b/g10/gpgv.c
index d9f2898..da07989 100644
--- a/g10/gpgv.c
+++ b/g10/gpgv.c
@@ -713,3 +713,12 @@ tofu_end_batch_update (ctrl_t ctrl)
 {
   (void)ctrl;
 }
+
+gpg_error_t
+tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb)
+{
+  (void) ctrl;
+  (void) kb;
+
+  return 0;
+}
diff --git a/g10/import.c b/g10/import.c
index 590959d..1ed11bf 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -111,7 +111,8 @@ static int import_secret_one (ctrl_t ctrl, kbnode_t keyblock,
                               struct import_stats_s *stats, int batch,
                               unsigned int options, int for_migration,
                               import_screener_t screener, void *screener_arg);
-static int import_revoke_cert (kbnode_t node, struct import_stats_s *stats);
+static int import_revoke_cert (ctrl_t ctrl,
+                               kbnode_t node, struct import_stats_s *stats);
 static int chk_self_sigs (kbnode_t keyblock, u32 *keyid, int *non_self);
 static int delete_inv_parts (kbnode_t keyblock,
                              u32 *keyid, unsigned int options);
@@ -562,7 +563,7 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats,
                                 screener, screener_arg);
       else if (keyblock->pkt->pkttype == PKT_SIGNATURE
                && keyblock->pkt->pkt.signature->sig_class == 0x20 )
-        rc = import_revoke_cert (keyblock, stats);
+        rc = import_revoke_cert (ctrl, keyblock, stats);
       else
         {
           log_info (_("skipping block of type %d\n"), keyblock->pkt->pkttype);
@@ -1642,7 +1643,7 @@ import_one (ctrl_t ctrl,
         {
           mod_key = 1;
           /* KEYBLOCK_ORIG has been updated; write */
-          rc = keydb_update_keyblock (hd, keyblock_orig);
+          rc = keydb_update_keyblock (ctrl, hd, keyblock_orig);
           if (rc)
             log_error (_("error writing keyring '%s': %s\n"),
                        keydb_get_resource_name (hd), gpg_strerror (rc) );
@@ -2288,7 +2289,7 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock,
  * Import a revocation certificate; this is a single signature packet.
  */
 static int
-import_revoke_cert (kbnode_t node, struct import_stats_s *stats)
+import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats)
 {
   PKT_public_key *pk = NULL;
   kbnode_t onode;
@@ -2379,7 +2380,7 @@ import_revoke_cert (kbnode_t node, struct import_stats_s *stats)
   insert_kbnode( keyblock, clone_kbnode(node), 0 );
 
   /* and write the keyblock back */
-  rc = keydb_update_keyblock (hd, keyblock );
+  rc = keydb_update_keyblock (ctrl, hd, keyblock );
   if (rc)
     log_error (_("error writing keyring '%s': %s\n"),
                keydb_get_resource_name (hd), gpg_strerror (rc) );
diff --git a/g10/keydb.c b/g10/keydb.c
index 1467b2d..aab90e3 100644
--- a/g10/keydb.c
+++ b/g10/keydb.c
@@ -1518,7 +1518,7 @@ build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus)
  * you should use keydb_push_found_state and keydb_pop_found_state to
  * save and restore it.  */
 gpg_error_t
-keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
+keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb)
 {
   gpg_error_t err;
   PKT_public_key *pk;
@@ -1542,6 +1542,10 @@ keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
   if (err)
     return err;
 
+#ifdef USE_TOFU
+  tofu_notice_key_changed (ctrl, kb);
+#endif
+
   memset (&desc, 0, sizeof (desc));
   fingerprint_from_pk (pk, desc.u.fpr, &len);
   if (len == 20)
diff --git a/g10/keydb.h b/g10/keydb.h
index e4fbe27..8daa9ee 100644
--- a/g10/keydb.h
+++ b/g10/keydb.h
@@ -181,7 +181,7 @@ const char *keydb_get_resource_name (KEYDB_HANDLE hd);
 gpg_error_t keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb);
 
 /* Update the keyblock KB.  */
-gpg_error_t keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb);
+gpg_error_t keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb);
 
 /* Insert a keyblock into one of the underlying keyrings or keyboxes.  */
 gpg_error_t keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb);
diff --git a/g10/keyedit.c b/g10/keyedit.c
index 795be05..5b77ee7 100644
--- a/g10/keyedit.c
+++ b/g10/keyedit.c
@@ -2782,7 +2782,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
 	case cmdSAVE:
 	  if (modified)
 	    {
-              err = keydb_update_keyblock (kdbhd, keyblock);
+              err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
               if (err)
                 {
                   log_error (_("update failed: %s\n"), gpg_strerror (err));
@@ -2936,7 +2936,7 @@ keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid)
 
   if (menu_adduid (ctrl, keyblock, 0, NULL, uidstring))
     {
-      err = keydb_update_keyblock (kdbhd, keyblock);
+      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
       if (err)
         {
           log_error (_("update failed: %s\n"), gpg_strerror (err));
@@ -3039,7 +3039,7 @@ keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev)
                          gpg_strerror (err));
               goto leave;
             }
-          err = keydb_update_keyblock (kdbhd, keyblock);
+          err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
           if (err)
             {
               log_error (_("update failed: %s\n"), gpg_strerror (err));
@@ -3261,7 +3261,7 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
 
   if (modified)
     {
-      err = keydb_update_keyblock (kdbhd, keyblock);
+      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
       if (err)
         {
           log_error (_("update failed: %s\n"), gpg_strerror (err));
@@ -3326,7 +3326,7 @@ keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr,
   /* Store.  */
   if (modified)
     {
-      err = keydb_update_keyblock (kdbhd, keyblock);
+      err = keydb_update_keyblock (ctrl, kdbhd, keyblock);
       if (err)
         {
           log_error (_("update failed: %s\n"), gpg_strerror (err));
diff --git a/g10/test-stubs.c b/g10/test-stubs.c
index 8560f9d..2dc65ab 100644
--- a/g10/test-stubs.c
+++ b/g10/test-stubs.c
@@ -517,3 +517,12 @@ tofu_end_batch_update (ctrl_t ctrl)
 {
   (void)ctrl;
 }
+
+gpg_error_t
+tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb)
+{
+  (void) ctrl;
+  (void) kb;
+
+  return 0;
+}
diff --git a/g10/tofu.c b/g10/tofu.c
index 696cfc3..9693893 100644
--- a/g10/tofu.c
+++ b/g10/tofu.c
@@ -682,13 +682,49 @@ initdb (sqlite3 *db)
     {
       /* Early version of the v1 format did not include the encryption
          table.  Add it.  */
-      sqlite3_exec (db,
-                    "create table if not exists encryptions"
-                    " (binding INTEGER NOT NULL,"
-                    "  time INTEGER);"
-                    "create index if not exists encryptions_binding"
-                    " on encryptions (binding);\n",
-                    NULL, NULL, &err);
+      rc = sqlite3_exec (db,
+                         "create table if not exists encryptions"
+                         " (binding INTEGER NOT NULL,"
+                         "  time INTEGER);"
+                         "create index if not exists encryptions_binding"
+                         " on encryptions (binding);\n",
+                         NULL, NULL, &err);
+      if (rc)
+        {
+	  log_error (_("error creating 'encryptions' TOFU table: %s\n"),
+		     err);
+          sqlite3_free (err);
+        }
+    }
+  if (! rc)
+    {
+      /* The effective policy for a binding.  If a key is ultimately
+       * trusted, then the effective policy of all of its bindings is
+       * good.  Likewise if a key is signed by an ultimately trusted
+       * key, etc.  If the effective policy is NONE, then we need to
+       * recompute the effective policy.  Otherwise, the effective
+       * policy is considered to be up to date, i.e., effective_policy
+       * is a cache of the computed policy.  */
+      rc = gpgsql_exec_printf
+        (db, NULL, NULL, &err,
+         "alter table bindings"
+         " add column effective_policy INTEGER"
+         " DEFAULT %d"
+         " CHECK (effective_policy in (%d, %d, %d, %d, %d, %d));",
+         TOFU_POLICY_NONE,
+         TOFU_POLICY_NONE, TOFU_POLICY_AUTO, TOFU_POLICY_GOOD,
+         TOFU_POLICY_UNKNOWN, TOFU_POLICY_BAD, TOFU_POLICY_ASK);
+      if (rc)
+	{
+          if (rc == SQLITE_ERROR)
+            /* Almost certainly "duplicate column name", which we can
+             * safely ignore.  */
+            rc = 0;
+          else
+            log_error (_("adding column effective_policy to bindings DB: %s\n"),
+                       err);
+	  sqlite3_free (err);
+	}
     }
 
   if (rc)
@@ -858,8 +894,9 @@ get_single_long_cb2 (void *cookie, int argc, char **argv, char **azColName,
    If SHOW_OLD is set, the binding's old policy is displayed.  */
 static gpg_error_t
 record_binding (tofu_dbs_t dbs, const char *fingerprint, const char *email,
-		const char *user_id, enum tofu_policy policy,
-                const char *conflict,
+		const char *user_id,
+                enum tofu_policy policy, enum tofu_policy effective_policy,
+                const char *conflict, int set_conflict,
                 int show_old, time_t now)
 {
   char *fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0);
@@ -924,19 +961,33 @@ record_binding (tofu_dbs_t dbs, const char *fingerprint, const char *email,
   rc = gpgsql_stepx
     (dbs->db, &dbs->s.record_binding_update, NULL, NULL, &err,
      "insert or replace into bindings\n"
-     " (oid, fingerprint, email, user_id, time, policy, conflict)\n"
+     " (oid, fingerprint, email, user_id, time,"
+     "  policy, conflict, effective_policy)\n"
      " values (\n"
      /* If we don't explicitly reuse the OID, then SQLite will
-	reallocate a new one.  We just need to search for the OID
-	based on the fingerprint and email since they are unique.  */
+      * reallocate a new one.  We just need to search for the OID
+      * based on the fingerprint and email since they are unique.  */
      "  (select oid from bindings where fingerprint = ? and email = ?),\n"
-     "  ?, ?, ?, ?, ?, ?);",
+     "  ?, ?, ?, ?, ?,"
+     /* If SET_CONFLICT is 0, then preserve conflict's current value.  */
+     "  case ?"
+     "    when 0 then"
+     "      (select conflict from bindings where fingerprint = ? and email = ?)"
+     "    else ?"
+     "  end,"
+     "  ?);",
+     /* oid subquery.  */
      GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email,
+     /* values 2 through 6.  */
      GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email,
      GPGSQL_ARG_STRING, user_id,
      GPGSQL_ARG_LONG_LONG, (long long) now,
      GPGSQL_ARG_INT, (int) policy,
+     /* conflict subquery.  */
+     GPGSQL_ARG_INT, set_conflict ? 1 : 0,
+     GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email,
      GPGSQL_ARG_STRING, conflict ? conflict : "",
+     GPGSQL_ARG_INT, (int) effective_policy,
      GPGSQL_ARG_END);
   if (rc)
     {
@@ -1113,108 +1164,6 @@ time_ago_scale (signed long t)
 }
 
 
-/* Return the policy for the binding <FINGERPRINT, EMAIL> (email has
-   already been normalized) and any conflict information in *CONFLICT
-   if CONFLICT is not NULL.  Returns _tofu_GET_POLICY_ERROR if an error
-   occurs.  */
-static enum tofu_policy
-get_policy (tofu_dbs_t dbs, const char *fingerprint, const char *email,
-	    char **conflict)
-{
-  int rc;
-  char *err = NULL;
-  strlist_t strlist = NULL;
-  enum tofu_policy policy = _tofu_GET_POLICY_ERROR;
-  long along;
-
-  /* Check if the <FINGERPRINT, EMAIL> binding is known
-     (TOFU_POLICY_NONE cannot appear in the DB.  Thus, if POLICY is
-     still TOFU_POLICY_NONE after executing the query, then the
-     result set was empty.)  */
-  rc = gpgsql_stepx (dbs->db, &dbs->s.get_policy_select_policy_and_conflict,
-                      strings_collect_cb2, &strlist, &err,
-                      "select policy, conflict from bindings\n"
-                      " where fingerprint = ? and email = ?",
-                      GPGSQL_ARG_STRING, fingerprint,
-                      GPGSQL_ARG_STRING, email,
-                      GPGSQL_ARG_END);
-  if (rc)
-    {
-      log_error (_("error reading TOFU database: %s\n"), err);
-      print_further_info ("checking for existing bad bindings");
-      sqlite3_free (err);
-      rc = gpg_error (GPG_ERR_GENERAL);
-      goto out;
-    }
-
-  if (strlist_length (strlist) == 0)
-    /* No results.  */
-    {
-      policy = TOFU_POLICY_NONE;
-      goto out;
-    }
-  else if (strlist_length (strlist) != 2)
-    /* The result has the wrong form.  */
-    {
-      log_error (_("error reading TOFU database: %s\n"),
-                 gpg_strerror (GPG_ERR_BAD_DATA));
-      print_further_info ("checking for existing bad bindings:"
-                          " expected 2 results, got %d\n",
-                          strlist_length (strlist));
-      goto out;
-    }
-
-  /* The result has the right form.  */
-
-  if (string_to_long (&along, strlist->d, 0, __LINE__))
-    {
-      log_error (_("error reading TOFU database: %s\n"),
-                 gpg_strerror (GPG_ERR_BAD_DATA));
-      print_further_info ("bad value for policy: %s", strlist->d);
-      goto out;
-    }
-  policy = along;
-
-  if (! (policy == TOFU_POLICY_AUTO
-	 || policy == TOFU_POLICY_GOOD
-	 || policy == TOFU_POLICY_UNKNOWN
-	 || policy == TOFU_POLICY_BAD
-	 || policy == TOFU_POLICY_ASK))
-    {
-      log_error (_("error reading TOFU database: %s\n"),
-                 gpg_strerror (GPG_ERR_DB_CORRUPTED));
-      print_further_info ("invalid value for policy (%d)", policy);
-      policy = _tofu_GET_POLICY_ERROR;
-      goto out;
-    }
-
-
-  /* If CONFLICT is set, then policy should be TOFU_POLICY_ASK.  But,
-     just in case, we do the check again here and ignore the conflict
-     if POLICY is not TOFU_POLICY_ASK.  */
-  if (conflict)
-    {
-      if (policy == TOFU_POLICY_ASK && *strlist->next->d)
-	*conflict = xstrdup (strlist->next->d);
-      else
-	*conflict = NULL;
-    }
-
- out:
-  log_assert (policy == _tofu_GET_POLICY_ERROR
-              || policy == TOFU_POLICY_NONE
-              || policy == TOFU_POLICY_AUTO
-              || policy == TOFU_POLICY_GOOD
-              || policy == TOFU_POLICY_UNKNOWN
-              || policy == TOFU_POLICY_BAD
-              || policy == TOFU_POLICY_ASK);
-
-  free_strlist (strlist);
-
-  return policy;
-}
-
-
 /* Format the first part of a conflict message and return that as a
  * malloced string.  */
 static char *
@@ -1862,7 +1811,7 @@ ask_about_binding (ctrl_t ctrl,
                 }
 
               if (record_binding (dbs, fingerprint, email, user_id,
-                                  *policy, NULL, 0, now))
+                                  *policy, TOFU_POLICY_NONE, NULL, 0, 0, now))
                 {
                   /* If there's an error registering the
                    * binding, don't save the signature.  */
@@ -2150,6 +2099,328 @@ build_conflict_set (tofu_dbs_t dbs,
 }
 
 
+/* Return the effective policy for the binding <FINGERPRINT, EMAIL>
+ * (email has already been normalized) and any conflict information in
+ * *CONFLICT_SETP, if CONFLICT_SETP is not NULL.  Returns
+ * _tofu_GET_POLICY_ERROR if an error occurs.  */
+static enum tofu_policy
+get_policy (tofu_dbs_t dbs, PKT_public_key *pk,
+            const char *fingerprint, const char *user_id, const char *email,
+	    strlist_t *conflict_setp, time_t now)
+{
+  int rc;
+  char *err = NULL;
+  strlist_t results = NULL;
+  enum tofu_policy policy = _tofu_GET_POLICY_ERROR;
+  enum tofu_policy effective_policy_orig = TOFU_POLICY_NONE;
+  enum tofu_policy effective_policy = _tofu_GET_POLICY_ERROR;
+  long along;
+  char *conflict_orig = NULL;
+  char *conflict = NULL;
+  strlist_t conflict_set = NULL;
+  int conflict_set_count;
+
+  /* Check if the <FINGERPRINT, EMAIL> binding is known
+     (TOFU_POLICY_NONE cannot appear in the DB.  Thus, if POLICY is
+     still TOFU_POLICY_NONE after executing the query, then the
+     result set was empty.)  */
+  rc = gpgsql_stepx (dbs->db, &dbs->s.get_policy_select_policy_and_conflict,
+                      strings_collect_cb2, &results, &err,
+                      "select policy, conflict, effective_policy from bindings\n"
+                      " where fingerprint = ? and email = ?",
+                      GPGSQL_ARG_STRING, fingerprint,
+                      GPGSQL_ARG_STRING, email,
+                      GPGSQL_ARG_END);
+  if (rc)
+    {
+      log_error (_("error reading TOFU database: %s\n"), err);
+      print_further_info ("reading the policy");
+      sqlite3_free (err);
+      rc = gpg_error (GPG_ERR_GENERAL);
+      goto out;
+    }
+
+  if (strlist_length (results) == 0)
+    {
+      /* No results.  Use the defaults.  */
+      policy = TOFU_POLICY_NONE;
+      effective_policy = TOFU_POLICY_NONE;
+    }
+  else if (strlist_length (results) == 3)
+    {
+      /* Parse and sanity check the results.  */
+
+      if (string_to_long (&along, results->d, 0, __LINE__))
+        {
+          log_error (_("error reading TOFU database: %s\n"),
+                     gpg_strerror (GPG_ERR_BAD_DATA));
+          print_further_info ("bad value for policy: %s", results->d);
+          goto out;
+        }
+      policy = along;
+
+      if (! (policy == TOFU_POLICY_AUTO
+             || policy == TOFU_POLICY_GOOD
+             || policy == TOFU_POLICY_UNKNOWN
+             || policy == TOFU_POLICY_BAD
+             || policy == TOFU_POLICY_ASK))
+        {
+          log_error (_("error reading TOFU database: %s\n"),
+                     gpg_strerror (GPG_ERR_DB_CORRUPTED));
+          print_further_info ("invalid value for policy (%d)", policy);
+          effective_policy = _tofu_GET_POLICY_ERROR;
+          goto out;
+        }
+
+      if (*results->next->d)
+        conflict = xstrdup (results->next->d);
+
+      if (string_to_long (&along, results->next->next->d, 0, __LINE__))
+        {
+          log_error (_("error reading TOFU database: %s\n"),
+                     gpg_strerror (GPG_ERR_BAD_DATA));
+          print_further_info ("bad value for effective policy: %s",
+                              results->next->next->d);
+          goto out;
+        }
+      effective_policy = along;
+
+      if (! (effective_policy == TOFU_POLICY_NONE
+             || effective_policy == TOFU_POLICY_AUTO
+             || effective_policy == TOFU_POLICY_GOOD
+             || effective_policy == TOFU_POLICY_UNKNOWN
+             || effective_policy == TOFU_POLICY_BAD
+             || effective_policy == TOFU_POLICY_ASK))
+        {
+          log_error (_("error reading TOFU database: %s\n"),
+                     gpg_strerror (GPG_ERR_DB_CORRUPTED));
+          print_further_info ("invalid value for effective_policy (%d)",
+                              effective_policy);
+          effective_policy = _tofu_GET_POLICY_ERROR;
+          goto out;
+        }
+    }
+  else
+    {
+      /* The result has the wrong form.  */
+
+      log_error (_("error reading TOFU database: %s\n"),
+                 gpg_strerror (GPG_ERR_BAD_DATA));
+      print_further_info ("reading policy: expected 3 columns, got %d\n",
+                          strlist_length (results));
+      goto out;
+    }
+
+  /* Save the effective policy and conflict so we know if we changed
+   * them.  */
+  effective_policy_orig = effective_policy;
+  conflict_orig = conflict;
+
+  /* Unless there is a conflict, if the effective policy is cached,
+   * just return it.  The reason we don't do this when there is a
+   * conflict is because of the following scenario: assume A and B
+   * conflict and B has signed A's key.  Now, later we import A's
+   * signature on B.  We need to recheck A, but the signature was on
+   * B, i.e., when B changes, we invalidate B's effective policy, but
+   * we also need to invalidate A's effective policy.  Instead, we
+   * assume that conflicts are rare and don't optimize for them, which
+   * would complicate the code.  */
+  if (effective_policy != TOFU_POLICY_NONE && !conflict)
+    goto out;
+
+  /* If the user explicitly set the policy, then respect that.  */
+  if (policy != TOFU_POLICY_AUTO && policy != TOFU_POLICY_NONE)
+    {
+      effective_policy = policy;
+      goto out;
+    }
+
+  /* Unless proven wrong, assume the effective policy is 'auto'.  */
+  effective_policy = TOFU_POLICY_AUTO;
+
+  /* See if the key is ultimately trusted.  */
+  {
+    u32 kid[2];
+
+    keyid_from_pk (pk, kid);
+    if (tdb_keyid_is_utk (kid))
+      {
+        effective_policy = TOFU_POLICY_GOOD;
+        goto out;
+      }
+  }
+
+  /* See if the key is signed by an ultimately trusted key.  */
+  {
+    int fingerprint_raw_len = strlen (fingerprint) / 2;
+    char fingerprint_raw[fingerprint_raw_len];
+    int len = 0;
+
+    if (fingerprint_raw_len != 20
+        || ((len = hex2bin (fingerprint,
+                            fingerprint_raw, fingerprint_raw_len))
+            != strlen (fingerprint)))
+      {
+        if (DBG_TRUST)
+          log_debug ("TOFU: Bad fingerprint: %s (len: %zd, parsed: %d)\n",
+                     fingerprint, strlen (fingerprint), len);
+      }
+    else
+      {
+        int lookup_err;
+        kbnode_t kb;
+
+        lookup_err = get_pubkey_byfprint (NULL, &kb,
+                                          fingerprint_raw,
+                                          fingerprint_raw_len);
+        if (lookup_err)
+          {
+            if (DBG_TRUST)
+              log_debug ("TOFU: Looking up %s: %s\n",
+                         fingerprint, gpg_strerror (lookup_err));
+          }
+        else
+          {
+            int is_signed_by_utk = signed_by_utk (email, kb);
+            release_kbnode (kb);
+            if (is_signed_by_utk)
+              {
+                effective_policy = TOFU_POLICY_GOOD;
+                goto out;
+              }
+          }
+      }
+  }
+
+  /* Check for any conflicts / see if a previously discovered conflict
+   * disappeared.  The latter can happen if the conflicting bindings
+   * are now cross signed, for instance.  */
+
+  conflict_set = build_conflict_set (dbs, pk, fingerprint, email);
+  conflict_set_count = strlist_length (conflict_set);
+  if (conflict_set_count == 0)
+    {
+      /* build_conflict_set should always at least return the current
+         binding.  Something went wrong.  */
+      effective_policy = _tofu_GET_POLICY_ERROR;
+      goto out;
+    }
+
+  if (conflict_set_count == 1
+      && (conflict_set->flags & BINDING_NEW))
+    {
+      /* We've never observed a binding with this email address and we
+       * have a default policy, which is not to ask the user.  */
+
+      /* If we've seen this binding, then we've seen this email and
+       * policy couldn't possibly be TOFU_POLICY_NONE.  */
+      log_assert (policy == TOFU_POLICY_NONE);
+
+      if (DBG_TRUST)
+	log_debug ("TOFU: New binding <key: %s, user id: %s>, no conflict.\n",
+		   fingerprint, email);
+
+      effective_policy = TOFU_POLICY_AUTO;
+      goto out;
+    }
+
+  if (conflict_set_count == 1
+      && (conflict_set->flags & BINDING_CONFLICT))
+    {
+      /* No known conflicts now, but there was a conflict.  That is,
+       * at somepoint there was a conflict, but it went away.  A
+       * conflict can go away if there is now a cross sig between the
+       * two keys.  In this case, we just silently clear the
+       * conflict.  */
+
+      if (DBG_TRUST)
+        log_debug ("TOFU: binding <key: %s, user id: %s> had a conflict, but it's been resolved (probably via  cross sig).\n",
+                   fingerprint, email);
+
+      effective_policy = TOFU_POLICY_AUTO;
+      conflict = NULL;
+
+      goto out;
+    }
+
+  if (conflict_set_count == 1)
+    {
+      /* No conflicts and never marked as conflicting.  */
+
+      log_assert (!conflict);
+
+      effective_policy = TOFU_POLICY_AUTO;
+
+      goto out;
+    }
+
+  /* There is a conflicting key.  */
+  log_assert (conflict_set_count > 1);
+  effective_policy = TOFU_POLICY_ASK;
+  conflict = xstrdup (conflict_set->next->d);
+
+ out:
+  log_assert (policy == _tofu_GET_POLICY_ERROR
+              || policy == TOFU_POLICY_NONE
+              || policy == TOFU_POLICY_AUTO
+              || policy == TOFU_POLICY_GOOD
+              || policy == TOFU_POLICY_UNKNOWN
+              || policy == TOFU_POLICY_BAD
+              || policy == TOFU_POLICY_ASK);
+  /* Everything but NONE.  */
+  log_assert (effective_policy == _tofu_GET_POLICY_ERROR
+              || effective_policy == TOFU_POLICY_AUTO
+              || effective_policy == TOFU_POLICY_GOOD
+              || effective_policy == TOFU_POLICY_UNKNOWN
+              || effective_policy == TOFU_POLICY_BAD
+              || effective_policy == TOFU_POLICY_ASK);
+
+  if (effective_policy != TOFU_POLICY_ASK && conflict)
+    conflict = NULL;
+
+  /* If we don't have a record of this binding, its effective policy
+   * changed, or conflict changed, update the DB.  */
+  if (effective_policy != _tofu_GET_POLICY_ERROR
+      && (/* New binding.  */
+          policy == TOFU_POLICY_NONE
+          /* effective_policy changed.  */
+          || effective_policy != effective_policy_orig
+          /* conflict changed.  */
+          || (conflict != conflict_orig
+              && (!conflict || !conflict_orig
+                  || strcmp (conflict, conflict_orig) != 0))))
+    {
+      if (record_binding (dbs, fingerprint, email, user_id,
+                          policy == TOFU_POLICY_NONE ? TOFU_POLICY_AUTO : policy,
+                          effective_policy, conflict, 1, 0, now) != 0)
+        log_error (_("error setting TOFU binding's policy"
+                     " to %s\n"), tofu_policy_str (policy));
+    }
+
+  /* If the caller wants the set of conflicts, return it.  */
+  if (effective_policy == TOFU_POLICY_ASK && conflict_setp)
+    {
+      if (! conflict_set)
+        conflict_set = build_conflict_set (dbs, pk, fingerprint, email);
+      *conflict_setp = conflict_set;
+    }
+  else
+    {
+      free_strlist (conflict_set);
+
+      if (conflict_setp)
+        *conflict_setp = NULL;
+    }
+
+  xfree (conflict_orig);
+  if (conflict != conflict_orig)
+    xfree (conflict);
+  free_strlist (results);
+
+  return effective_policy;
+}
+
+
 /* Return the trust level (TRUST_NEVER, etc.) for the binding
  * <FINGERPRINT, EMAIL> (email is already normalized).  If no policy
  * is registered, returns TOFU_POLICY_NONE.  If an error occurs,
@@ -2175,9 +2446,7 @@ get_trust (ctrl_t ctrl, PKT_public_key *pk,
   enum tofu_policy policy;
   int rc;
   char *sqerr = NULL;
-  int change_conflicting_to_ask = 0;
   strlist_t conflict_set = NULL;
-  int conflict_set_count;
   int trust_level = TRUST_UNKNOWN;
   strlist_t iter;
 
@@ -2201,36 +2470,22 @@ get_trust (ctrl_t ctrl, PKT_public_key *pk,
               && _tofu_GET_TRUST_ERROR != TRUST_FULLY
               && _tofu_GET_TRUST_ERROR != TRUST_ULTIMATE);
 
-  begin_transaction (ctrl, 0);
-  in_transaction = 1;
-
-  policy = get_policy (dbs, fingerprint, email, NULL);
+  /* If the key is ultimately trusted, there is nothing to do.  */
   {
-    /* See if the key is ultimately trusted.  If so, we're done.  */
     u32 kid[2];
 
     keyid_from_pk (pk, kid);
-
     if (tdb_keyid_is_utk (kid))
       {
-        if (policy == TOFU_POLICY_NONE)
-          /* New binding.  */
-          {
-            if (record_binding (dbs, fingerprint, email, user_id,
-                                TOFU_POLICY_GOOD, NULL, 0, now) != 0)
-              {
-                log_error (_("error setting TOFU binding's trust level"
-                             " to %s\n"), "good");
-                trust_level = _tofu_GET_TRUST_ERROR;
-                goto out;
-              }
-          }
-
         trust_level = TRUST_ULTIMATE;
         goto out;
       }
   }
 
+  begin_transaction (ctrl, 0);
+  in_transaction = 1;
+
+  policy = get_policy (dbs, pk, fingerprint, user_id, email, &conflict_set, now);
   if (policy == TOFU_POLICY_AUTO)
     {
       policy = opt.tofu_default_policy;
@@ -2255,12 +2510,7 @@ get_trust (ctrl_t ctrl, PKT_public_key *pk,
       goto out;
 
     case TOFU_POLICY_ASK:
-      /* We need to ask the user what to do.  Case #1 or #2 below.  */
-      break;
-
-    case TOFU_POLICY_NONE:
-      /* The binding is new, we need to check for conflicts.  Case #3
-       * below.  */
+      /* We need to ask the user what to do.  */
       break;
 
     case _tofu_GET_POLICY_ERROR:
@@ -2281,211 +2531,68 @@ get_trust (ctrl_t ctrl, PKT_public_key *pk,
    *   2. The saved policy is ask (either last time the user selected
    *      accept once or reject once or there was a conflict and this
    *      binding's policy was changed from auto to ask)
-   *      (policy == TOFU_POLICY_ASK), or,
-   *
-   *   3. We don't have a saved policy (policy == TOFU_POLICY_NONE)
-   *      (need to check for a conflict).
-   *
-   * In summary: POLICY is ask or none.
+   *      (policy == TOFU_POLICY_ASK).
    */
+  log_assert (policy == TOFU_POLICY_ASK);
 
-  /* Before continuing, see if the key is signed by an ultimately
-   * trusted key.  */
-  {
-    int fingerprint_raw_len = strlen (fingerprint) / 2;
-    char fingerprint_raw[fingerprint_raw_len];
-    int len = 0;
-    int is_signed_by_utk = 0;
-
-    if (fingerprint_raw_len != 20
-        || ((len = hex2bin (fingerprint,
-                            fingerprint_raw, fingerprint_raw_len))
-            != strlen (fingerprint)))
-      {
-        if (DBG_TRUST)
-          log_debug ("TOFU: Bad fingerprint: %s (len: %zd, parsed: %d)\n",
-                     fingerprint, strlen (fingerprint), len);
-      }
-    else
-      {
-        int lookup_err;
-        kbnode_t kb;
-
-        lookup_err = get_pubkey_byfprint (NULL, &kb,
-                                          fingerprint_raw,
-                                          fingerprint_raw_len);
-        if (lookup_err)
-          {
-            if (DBG_TRUST)
-              log_debug ("TOFU: Looking up %s: %s\n",
-                         fingerprint, gpg_strerror (lookup_err));
-          }
-        else
-          {
-            is_signed_by_utk = signed_by_utk (email, kb);
-            release_kbnode (kb);
-          }
-      }
-
-    if (is_signed_by_utk)
-      {
-        if (record_binding (dbs, fingerprint, email, user_id,
-                            TOFU_POLICY_GOOD, NULL, 0, now) != 0)
-          {
-            log_error (_("error setting TOFU binding's trust level"
-                         " to %s\n"), "good");
-            trust_level = _tofu_GET_TRUST_ERROR;
-          }
-        else
-          trust_level = TRUST_FULLY;
-
-        goto out;
-      }
-  }
-
-
-  /* Look for conflicts.  This is needed in all 3 cases.  */
-  conflict_set = build_conflict_set (dbs, pk, fingerprint, email);
-  conflict_set_count = strlist_length (conflict_set);
-  if (conflict_set_count == 0)
-    {
-      /* We should always at least have the current binding.  */
-      trust_level = _tofu_GET_TRUST_ERROR;
-      goto out;
-    }
-
-  if (conflict_set_count == 1
-      && (conflict_set->flags & BINDING_NEW)
-      && opt.tofu_default_policy != TOFU_POLICY_ASK)
+  if (may_ask)
     {
-      /* We've never observed a binding with this email address and we
-       * have a default policy, which is not to ask the user.  */
-
-      /* If we've seen this binding, then we've seen this email and
-       * policy couldn't possibly be TOFU_POLICY_NONE.  */
-      log_assert (policy == TOFU_POLICY_NONE);
-
-      if (DBG_TRUST)
-	log_debug ("TOFU: New binding <key: %s, user id: %s>, no conflict.\n",
-		   fingerprint, email);
-
-      if (record_binding (dbs, fingerprint, email, user_id,
-			  TOFU_POLICY_AUTO, NULL, 0, now) != 0)
-	{
-	  log_error (_("error setting TOFU binding's trust level to %s\n"),
-		       "auto");
-	  trust_level = _tofu_GET_TRUST_ERROR;
-	  goto out;
-	}
+      /* We can't be in a normal transaction in ask_about_binding.  */
+      end_transaction (ctrl, 0);
+      in_transaction = 0;
 
-      trust_level = tofu_policy_to_trust_level (TOFU_POLICY_AUTO);
-      goto out;
+      /* If we get here, we need to ask the user about the binding.  */
+      ask_about_binding (ctrl,
+                         &policy,
+                         &trust_level,
+                         conflict_set,
+                         fingerprint,
+                         email,
+                         user_id,
+                         now);
     }
+  else
+    trust_level = TRUST_UNDEFINED;
 
-  if (conflict_set_count == 1
-      && (conflict_set->flags & BINDING_CONFLICT))
+  /* Mark any conflicting bindings that have an automatic policy as
+   * now requiring confirmation.  Note: we do this after we ask for
+   * confirmation so that when the current policy is printed, it is
+   * correct.  */
+  if (! in_transaction)
     {
-      /* No known conflicts now, but there was a conflict.  This means
-       * at somepoint, there was a conflict and we changed this
-       * binding's policy to ask and set the conflicting key.  The
-       * conflict can go away if there is not a cross sig between the
-       * two keys.  In this case, just silently clear the conflict and
-       * reset the policy to auto.  */
-
-      log_assert (policy == TOFU_POLICY_ASK);
-
-      if (DBG_TRUST)
-        log_debug ("TOFU: binding <key: %s, user id: %s> had a conflict, but it's been resolved (probably via  cross sig).\n",
-                   fingerprint, email);
-
-      if (record_binding (dbs, fingerprint, email, user_id,
-			  TOFU_POLICY_AUTO, NULL, 0, now) != 0)
-	log_error (_("error setting TOFU binding's trust level to %s\n"),
-		   "auto");
-
-      trust_level = tofu_policy_to_trust_level (TOFU_POLICY_AUTO);
-      goto out;
+      begin_transaction (ctrl, 0);
+      in_transaction = 1;
     }
 
-  /* We have a conflict.  Mark any conflicting bindings that have an
-   * automatic policy as now requiring confirmation.  Note: we delay
-   * this until after we ask for confirmation so that when the current
-   * policy is printed, it is correct.  */
-  change_conflicting_to_ask = 1;
+  /* The conflict set should always contain at least one element:
+   * the current key.  */
+  log_assert (conflict_set);
 
-  if (! may_ask)
+  for (iter = conflict_set->next; iter; iter = iter->next)
     {
-      log_assert (policy == TOFU_POLICY_NONE || policy == TOFU_POLICY_ASK);
-      if (policy == TOFU_POLICY_NONE)
+      /* We don't immediately set the effective policy to 'ask,
+         because  */
+      rc = gpgsql_exec_printf
+        (dbs->db, NULL, NULL, &sqerr,
+         "update bindings set effective_policy = %d, conflict = %Q"
+         " where email = %Q and fingerprint = %Q and effective_policy != %d;",
+         TOFU_POLICY_NONE, fingerprint,
+         email, iter->d, TOFU_POLICY_ASK);
+      if (rc)
         {
-          /* We get here in the third case (no saved policy) and if
-           * there is a conflict.  */
-          if (record_binding (dbs, fingerprint, email, user_id,
-                              TOFU_POLICY_ASK,
-                              conflict_set && conflict_set->next
-                              ? conflict_set->next->d : NULL,
-                              0, now) != 0)
-            log_error (_("error setting TOFU binding's trust level to %s\n"),
-                       "ask");
+          log_error (_("error changing TOFU policy: %s\n"), sqerr);
+          print_further_info ("binding: <key: %s, user id: %s>",
+                              fingerprint, user_id);
+          sqlite3_free (sqerr);
+          sqerr = NULL;
+          rc = gpg_error (GPG_ERR_GENERAL);
         }
-
-      trust_level = TRUST_UNDEFINED;
-      goto out;
+      else if (DBG_TRUST)
+        log_debug ("Set %s to conflict with %s\n",
+                   iter->d, fingerprint);
     }
 
-  /* We can't be in a normal transaction in ask_about_binding.  */
-  end_transaction (ctrl, 0);
-  in_transaction = 0;
-
-  /* If we get here, we need to ask the user about the binding.  */
-  ask_about_binding (ctrl,
-                     &policy,
-                     &trust_level,
-                     conflict_set,
-                     fingerprint,
-                     email,
-                     user_id,
-                     now);
-
  out:
-
-  if (change_conflicting_to_ask)
-    {
-      /* Mark any conflicting bindings that have an automatic policy as
-       * now requiring confirmation.  */
-
-      if (! in_transaction)
-        {
-          begin_transaction (ctrl, 0);
-          in_transaction = 1;
-        }
-
-      /* If we weren't allowed to ask, also update this key as
-       * conflicting with itself.  */
-      for (iter = may_ask ? conflict_set->next : conflict_set;
-           iter; iter = iter->next)
-        {
-          rc = gpgsql_exec_printf
-            (dbs->db, NULL, NULL, &sqerr,
-             "update bindings set policy = %d, conflict = %Q"
-             " where email = %Q and fingerprint = %Q and policy = %d;",
-             TOFU_POLICY_ASK, fingerprint,
-             email, iter->d, TOFU_POLICY_AUTO);
-          if (rc)
-            {
-              log_error (_("error changing TOFU policy: %s\n"), sqerr);
-              print_further_info ("binding: <key: %s, user id: %s>",
-                                  fingerprint, user_id);
-              sqlite3_free (sqerr);
-              sqerr = NULL;
-              rc = gpg_error (GPG_ERR_GENERAL);
-            }
-          else if (DBG_TRUST)
-            log_debug ("Set %s to conflict with %s\n",
-                       iter->d, fingerprint);
-        }
-    }
-
   if (in_transaction)
     end_transaction (ctrl, 0);
 
@@ -2684,17 +2791,18 @@ write_stats_status (estream_t fp,
 }
 
 /* Note: If OUTFP is not NULL, this function merely prints a "tfs" record
- * to OUTFP.  In this case USER_ID is not required.
+ * to OUTFP.
  *
  * Returns whether the caller should call show_warning after iterating
  * over all user ids.
  */
 static int
-show_statistics (tofu_dbs_t dbs, const char *fingerprint,
+show_statistics (tofu_dbs_t dbs, PKT_public_key *pk, const char *fingerprint,
 		 const char *email, const char *user_id,
 		 estream_t outfp, time_t now)
 {
-  enum tofu_policy policy = get_policy (dbs, fingerprint, email, NULL);
+  enum tofu_policy policy =
+    get_policy (dbs, pk, fingerprint, user_id, email, NULL, now);
 
   char *fingerprint_pp;
   int rc;
@@ -3336,7 +3444,7 @@ tofu_write_tfs_record (ctrl_t ctrl, estream_t fp,
   fingerprint = hexfingerprint (pk, NULL, 0);
   email = email_from_user_id (user_id);
 
-  show_statistics (dbs, fingerprint, email, user_id, fp, now);
+  show_statistics (dbs, pk, fingerprint, email, user_id, fp, now);
 
   xfree (email);
   xfree (fingerprint);
@@ -3412,7 +3520,7 @@ tofu_get_validity (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list,
 
       if (may_ask && tl != TRUST_ULTIMATE && tl != TRUST_EXPIRED)
         need_warning |=
-          show_statistics (dbs, fingerprint, email, user_id->d, NULL, now);
+          show_statistics (dbs, pk, fingerprint, email, user_id->d, NULL, now);
 
       if (tl == TRUST_NEVER)
         trust_level = TRUST_NEVER;
@@ -3512,7 +3620,7 @@ tofu_set_policy (ctrl_t ctrl, kbnode_t kb, enum tofu_policy policy)
       email = email_from_user_id (user_id->name);
 
       err = record_binding (dbs, fingerprint, email, user_id->name,
-                            policy, NULL, 1, now);
+                            policy, TOFU_POLICY_NONE, NULL, 0, 1, now);
       if (err)
         {
           log_error (_("error setting policy for key %s, user id \"%s\": %s"),
@@ -3561,6 +3669,7 @@ gpg_error_t
 tofu_get_policy (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *user_id,
 		 enum tofu_policy *policy)
 {
+  time_t now = gnupg_get_time ();
   tofu_dbs_t dbs;
   char *fingerprint;
   char *email;
@@ -3580,7 +3689,7 @@ tofu_get_policy (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *user_id,
 
   email = email_from_user_id (user_id->name);
 
-  *policy = get_policy (dbs, fingerprint, email, NULL);
+  *policy = get_policy (dbs, pk, fingerprint, user_id->name, email, NULL, now);
 
   xfree (email);
   xfree (fingerprint);
@@ -3588,3 +3697,42 @@ tofu_get_policy (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *user_id,
     return gpg_error (GPG_ERR_GENERAL);
   return 0;
 }
+
+gpg_error_t
+tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb)
+{
+  tofu_dbs_t dbs;
+  PKT_public_key *pk;
+  char *fingerprint;
+  char *sqlerr = NULL;
+  int rc;
+
+  /* Make sure PK is a primary key.  */
+  setup_main_keyids (kb);
+  pk = kb->pkt->pkt.public_key;
+  log_assert (pk_is_primary (pk));
+
+  fingerprint = hexfingerprint (pk, NULL, 0);
+
+  dbs = opendbs (ctrl);
+  if (! dbs)
+    {
+      log_error (_("error opening TOFU database: %s\n"),
+                 gpg_strerror (GPG_ERR_GENERAL));
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+
+  fingerprint = hexfingerprint (pk, NULL, 0);
+
+  rc = gpgsql_stepx (dbs->db, NULL, NULL, NULL, &sqlerr,
+                     "update bindings set effective_policy = ?"
+                     " where fingerprint = ?;",
+                     GPGSQL_ARG_INT, (int) TOFU_POLICY_NONE,
+                     GPGSQL_ARG_STRING, fingerprint,
+                     GPGSQL_ARG_END);
+  xfree (fingerprint);
+
+  if (rc == _tofu_GET_POLICY_ERROR)
+    return gpg_error (GPG_ERR_GENERAL);
+  return 0;
+}
diff --git a/g10/tofu.h b/g10/tofu.h
index f114443..3ee2f41 100644
--- a/g10/tofu.h
+++ b/g10/tofu.h
@@ -139,4 +139,9 @@ void tofu_end_batch_update (ctrl_t ctrl);
 /* Release all of the resources associated with a DB meta-handle.  */
 void tofu_closedbs (ctrl_t ctrl);
 
+/* Whenever a key is modified (e.g., a user id is added or revoked, a
+ * new signature, etc.), this function should be called to cause TOFU
+ * to update its world view.  */
+gpg_error_t tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb);
+
 #endif /*G10_TOFU_H*/
diff --git a/tests/openpgp/tofu.scm b/tests/openpgp/tofu.scm
index 91c9e78..2a04d13 100755
--- a/tests/openpgp/tofu.scm
+++ b/tests/openpgp/tofu.scm
@@ -20,7 +20,7 @@
 (load (with-path "defs.scm"))
 (setup-environment)
 
- ;; Redefine GPG without --always-trust and a fixed time.
+;; Redefine GPG without --always-trust and a fixed time.
 (define GPG `(,(tool 'gpg) --no-permission-warning
 	      --faked-system-time=1466684990))
 (define GNUPGHOME (getenv "GNUPGHOME"))
@@ -141,20 +141,23 @@
       '("auto" "good" "unknown" "bad" "ask"))))
  '("good" "unknown" "bad"))
 
-;; BC15C85A conflicts with 2183839A.  On conflict, this will set
-;; BC15C85A to ask.  If 2183839A is auto (it's not, it's bad), then
-;; it will be set to ask.
-(call-check `(, at GPG --trust-model=tofu
-		    --verify ,(in-srcdir "tofu-BC15C85A-1.txt")))
+;; At the end, 2183839A's policy should be bad.
+(checkpolicy "2183839A" "bad")
+
+;; BC15C85A and 2183839A conflict.  A policy setting of "auto"
+;; (BC15C85A's state) will result in an effective policy of ask.  But,
+;; a policy setting of "bad" will result in an effective policy of
+;; bad.
+(setpolicy "BC15C85A" "auto")
 (checkpolicy "BC15C85A" "ask")
 (checkpolicy "2183839A" "bad")
 
-;; EE37CF96 conflicts with 2183839A and BC15C85A.  We change
-;; BC15C85A's policy to auto and leave 2183839A's policy at bad.
-;; This conflict should cause BC15C85A's policy to be changed to
-;; ask (since it is auto), but not affect 2183839A's policy.
+;; EE37CF96, 2183839A, and BC15C85A conflict.  We change BC15C85A's
+;; policy to auto and leave 2183839A's policy at bad.  This conflict
+;; should cause BC15C85A's policy to be changed to ask (since it is
+;; auto), but not affect 2183839A's policy.
 (setpolicy "BC15C85A" "auto")
-(checkpolicy "BC15C85A" "auto")
+(checkpolicy "BC15C85A" "ask")
 (call-check `(, at GPG --trust-model=tofu
 		    --verify ,(in-srcdir "tofu-EE37CF96-1.txt")))
 (checkpolicy "BC15C85A" "ask")
@@ -225,7 +228,8 @@
 (checkpolicy KEYA "ask")
 (checkpolicy KEYB "ask")
 
-;; Import Alice's signature on the conflicting user id.
+;; Import Alice's signature on the conflicting user id.  Since there
+;; is now a cross signature, we should revert to the default policy.
 (display "    > Adding cross signature on user id. ")
 (call-check `(, at GPG --import ,(in-srcdir DIR (string-append KEYIDB "-4.gpg"))))
 (verify-messages)

commit 182efc5b5d20ac0d43501a22f349a23dc06a27a4
Author: Neal H. Walfield <neal at g10code.com>
Date:   Mon Nov 21 21:22:02 2016 +0100

    g10: Correctly parameterize ngettext.
    
    * g10/tofu.c (ask_about_binding): Correctly parameterize ngettext.
    
    Signed-off-by: Neal H. Walfield <neal at g10code.com>

diff --git a/g10/tofu.c b/g10/tofu.c
index deccc0f..696cfc3 100644
--- a/g10/tofu.c
+++ b/g10/tofu.c
@@ -1733,19 +1733,22 @@ ask_about_binding (ctrl_t ctrl,
               if (labs(stats_iter->time_ago) == 3)
                 es_fprintf (fp, ngettext(" over the past day.",
                                          " over the past %d days.",
-                                         seen_in_past),
+                                         TIME_AGO_SMALL_THRESHOLD
+                                         / TIME_AGO_UNIT_SMALL),
                             TIME_AGO_SMALL_THRESHOLD
                             / TIME_AGO_UNIT_SMALL);
               else if (labs(stats_iter->time_ago) == 4)
                 es_fprintf (fp, ngettext(" over the past month.",
                                          " over the past %d months.",
-                                         seen_in_past),
+                                         TIME_AGO_MEDIUM_THRESHOLD
+                                         / TIME_AGO_UNIT_MEDIUM),
                             TIME_AGO_MEDIUM_THRESHOLD
                             / TIME_AGO_UNIT_MEDIUM);
               else if (labs(stats_iter->time_ago) == 5)
                 es_fprintf (fp, ngettext(" over the past year.",
                                          " over the past %d years.",
-                                         seen_in_past),
+                                         TIME_AGO_LARGE_THRESHOLD
+                                         / TIME_AGO_UNIT_LARGE),
                             TIME_AGO_LARGE_THRESHOLD
                             / TIME_AGO_UNIT_LARGE);
               else if (labs(stats_iter->time_ago) == 6)

commit 7142b293c870d73ce0146bfb90e6a556e0079650
Author: Neal H. Walfield <neal at g10code.com>
Date:   Mon Nov 21 21:13:15 2016 +0100

    g10: Don't use the same variable for multiple SQL compiled statements
    
    * g10/tofu.c (struct tofu_dbs_s): Remove unused field
    record_binding_update2.  Replace register_insert with
    register_signature and register_encryption.
    (tofu_register_signature): Don't use dbs->s.register_insert, but
    dbs->s.register_signature.
    (tofu_register_encryption): Don't use dbs->s.register_insert, but
    dbs->s.register_encryption.
    
    Signed-off-by: Neal H. Walfield <neal at g10code.com>

diff --git a/g10/tofu.c b/g10/tofu.c
index 05d171d..deccc0f 100644
--- a/g10/tofu.c
+++ b/g10/tofu.c
@@ -79,14 +79,14 @@ struct tofu_dbs_s
 
     sqlite3_stmt *record_binding_get_old_policy;
     sqlite3_stmt *record_binding_update;
-    sqlite3_stmt *record_binding_update2;
     sqlite3_stmt *get_policy_select_policy_and_conflict;
     sqlite3_stmt *get_trust_bindings_with_this_email;
     sqlite3_stmt *get_trust_gather_other_user_ids;
     sqlite3_stmt *get_trust_gather_signature_stats;
     sqlite3_stmt *get_trust_gather_encryption_stats;
     sqlite3_stmt *register_already_seen;
-    sqlite3_stmt *register_insert;
+    sqlite3_stmt *register_signature;
+    sqlite3_stmt *register_encryption;
   } s;
 
   int in_batch_transaction;
@@ -3093,7 +3093,7 @@ tofu_register_signature (ctrl_t ctrl,
           log_assert (c == 0);
 
           rc = gpgsql_stepx
-            (dbs->db, &dbs->s.register_insert, NULL, NULL, &err,
+            (dbs->db, &dbs->s.register_signature, NULL, NULL, &err,
              "insert into signatures\n"
              " (binding, sig_digest, origin, sig_time, time)\n"
              " values\n"
@@ -3209,7 +3209,7 @@ tofu_register_encryption (ctrl_t ctrl,
         }
 
       rc = gpgsql_stepx
-        (dbs->db, &dbs->s.register_insert, NULL, NULL, &err,
+        (dbs->db, &dbs->s.register_encryption, NULL, NULL, &err,
          "insert into encryptions\n"
          " (binding, time)\n"
          " values\n"

commit 91a0483c5db8ee4510981448a705981ee1cce199
Author: Neal H. Walfield <neal at g10code.com>
Date:   Mon Nov 21 16:19:32 2016 +0100

    g10: Add a convenience function for checking if a key is a primary key
    
    * g10/keydb.h (pk_is_primary): New function.
    * g10/tofu.c (get_trust): Use it.
    (tofu_register_signature): Likewise.
    (tofu_register_encryption): Likewise.
    (tofu_set_policy): Likewise.
    (tofu_get_policy): Likewise.
    
    Signed-off-by: Neal H. Walfield <neal at g10code.com>

diff --git a/g10/keydb.h b/g10/keydb.h
index 815b17e..e4fbe27 100644
--- a/g10/keydb.h
+++ b/g10/keydb.h
@@ -433,6 +433,13 @@ keyid_cmp (const u32 *a, const u32 *b)
   return 0;
 }
 
+/* Return whether PK is a primary key.  */
+static int GPGRT_ATTR_UNUSED
+pk_is_primary (PKT_public_key *pk)
+{
+  return keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) == 0;
+}
+
 /* Copy the keyid in SRC to DEST and return DEST.  */
 u32 *keyid_copy (u32 *dest, const u32 *src);
 
diff --git a/g10/tofu.c b/g10/tofu.c
index 2c9d17c..05d171d 100644
--- a/g10/tofu.c
+++ b/g10/tofu.c
@@ -2186,7 +2186,7 @@ get_trust (ctrl_t ctrl, PKT_public_key *pk,
   if (opt.batch)
     may_ask = 0;
 
-  log_assert (keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) == 0);
+  log_assert (pk_is_primary (pk));
 
   /* Make sure _tofu_GET_TRUST_ERROR isn't equal to any of the trust
      levels.  */
@@ -3009,7 +3009,7 @@ tofu_register_signature (ctrl_t ctrl,
   if (rc)
     return rc;
 
-  log_assert (keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) == 0);
+  log_assert (pk_is_primary (pk));
 
   sig_digest = make_radix64_string (sig_digest_bin, sig_digest_bin_len);
   fingerprint = hexfingerprint (pk, NULL, 0);
@@ -3155,13 +3155,13 @@ tofu_register_encryption (ctrl_t ctrl,
     }
 
   if (/* We need the key block to find the primary key.  */
-      keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) != 0
+      ! pk_is_primary (pk)
       /* We need the key block to find all user ids.  */
       || ! user_id_list)
     kb = get_pubkeyblock (pk->keyid);
 
   /* Make sure PK is a primary key.  */
-  if (keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) != 0)
+  if (! pk_is_primary (pk))
     pk = kb->pkt->pkt.public_key;
 
   if (! user_id_list)
@@ -3485,7 +3485,7 @@ tofu_set_policy (ctrl_t ctrl, kbnode_t kb, enum tofu_policy policy)
   if (DBG_TRUST)
     log_debug ("Setting TOFU policy for %s to %s\n",
 	       keystr (pk->keyid), tofu_policy_str (policy));
-  if (keyid_cmp (pk_main_keyid (pk), pk_keyid (pk)) != 0)
+  if (! pk_is_primary (pk))
     log_bug ("%s: Passed a subkey, but expecting a primary key.\n", __func__);
 
   fingerprint = hexfingerprint (pk, NULL, 0);
@@ -3563,8 +3563,7 @@ tofu_get_policy (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *user_id,
   char *email;
 
   /* Make sure PK is a primary key.  */
-  log_assert (pk->main_keyid[0] == pk->keyid[0]
-              && pk->main_keyid[1] == pk->keyid[1]);
+  log_assert (pk_is_primary (pk));
 
   dbs = opendbs (ctrl);
   if (! dbs)

-----------------------------------------------------------------------

Summary of changes:
 g10/gpgv.c             |   9 +
 g10/import.c           |  11 +-
 g10/keydb.c            |   6 +-
 g10/keydb.h            |   9 +-
 g10/keyedit.c          |  10 +-
 g10/test-stubs.c       |   9 +
 g10/tofu.c             | 856 +++++++++++++++++++++++++++++--------------------
 g10/tofu.h             |   5 +
 tests/openpgp/tofu.scm |  28 +-
 9 files changed, 566 insertions(+), 377 deletions(-)


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list