g10: If the set of UTKs changes, invalidate any changed policies.
authorNeal H. Walfield <neal@g10code.com>
Tue, 22 Nov 2016 14:05:59 +0000 (15:05 +0100)
committerNeal H. Walfield <neal@g10code.com>
Tue, 22 Nov 2016 14:24:05 +0000 (15:24 +0100)
* g10/trustdb.c (tdb_utks): New function.
* g10/tofu.c (check_utks): New function.
(initdb): Call it.
* tests/openpgp/tofu.scm: Modify test to check the effective policy of
keys whose effective policy changes when we change the set of UTKs.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
If the set of ultimately trusted keys changes, then it is possible
that a binding's effective policy changes.  To deal with this, we
detect when the set of ultimately trusted keys changes and invalidate
all cached policies.

g10/tofu.c
g10/trustdb.c
g10/trustdb.h
tests/openpgp/tofu.scm

index 6eb7f5e..d7730a3 100644 (file)
@@ -506,6 +506,152 @@ version_check_cb (void *cookie, int argc, char **argv, char **azColName)
   return 1;
 }
 
+static int
+check_utks (sqlite3 *db)
+{
+  int rc;
+  char *err = NULL;
+  struct key_item *utks;
+  struct key_item *ki;
+  int utk_count;
+  char *utks_string = NULL;
+  char keyid_str[16+1];
+  long utks_unchanged = 0;
+
+  /* An early version of the v1 format did not include the list of
+   * known ultimately trusted keys.
+   *
+   * This list is used to detect when the set of ultimately trusted
+   * keys changes.  We need to detect this to invalidate the effective
+   * policy, which can change if an ultimately trusted key is added or
+   * removed.  */
+  rc = sqlite3_exec (db,
+                     "create table if not exists ultimately_trusted_keys"
+                     " (keyid);\n",
+                     NULL, NULL, &err);
+  if (rc)
+    {
+      log_error (_("error creating 'ultimately_trusted_keys' TOFU table: %s\n"),
+                 err);
+      sqlite3_free (err);
+      goto out;
+    }
+
+
+  utks = tdb_utks ();
+  for (ki = utks, utk_count = 0; ki; ki = ki->next, utk_count ++)
+    ;
+
+  if (utk_count)
+    {
+      /* Build a list of keyids of the form "XXX","YYY","ZZZ".  */
+      int len = (1 + 16 + 1 + 1) * utk_count;
+      int o = 0;
+
+      utks_string = xmalloc (len);
+      *utks_string = 0;
+      for (ki = utks, utk_count = 0; ki; ki = ki->next, utk_count ++)
+        {
+          utks_string[o ++] = '\'';
+          format_keyid (ki->kid, KF_LONG,
+                        keyid_str, sizeof (keyid_str));
+          memcpy (&utks_string[o], keyid_str, 16);
+          o += 16;
+          utks_string[o ++] = '\'';
+          utks_string[o ++] = ',';
+        }
+      utks_string[o - 1] = 0;
+      log_assert (o == len);
+    }
+
+  rc = gpgsql_exec_printf
+    (db, get_single_unsigned_long_cb, &utks_unchanged, &err,
+     "select"
+     /* Removed UTKs?  (Known UTKs in current UTKs.)  */
+     "  ((select count(*) from ultimately_trusted_keys"
+     "     where (keyid in (%s))) == %d)"
+     " and"
+     /* New UTKs?  */
+     "  ((select count(*) from ultimately_trusted_keys"
+     "     where keyid not in (%s)) == 0);",
+     utks_string ? utks_string : "",
+     utk_count,
+     utks_string ? utks_string : "");
+  xfree (utks_string);
+  if (rc)
+    {
+      log_error (_("TOFU DB error"));
+      print_further_info ("checking if ultimately trusted keys changed: %s",
+                         err);
+      sqlite3_free (err);
+      goto out;
+    }
+
+  if (utks_unchanged)
+    goto out;
+
+  if (DBG_TRUST)
+    log_debug ("TOFU: ultimately trusted keys changed.\n");
+
+  /* Given that the set of ultimately trusted keys
+   * changed, clear any cached policies.  */
+  rc = gpgsql_exec_printf
+    (db, NULL, NULL, &err,
+     "update bindings set effective_policy = %d;",
+     TOFU_POLICY_NONE);
+  if (rc)
+    {
+      log_error (_("TOFU DB error"));
+      print_further_info ("clearing cached policies: %s", err);
+      sqlite3_free (err);
+      goto out;
+    }
+
+  /* Now, update the UTK table.  */
+  rc = sqlite3_exec (db,
+                     "drop table ultimately_trusted_keys;",
+                     NULL, NULL, &err);
+  if (rc)
+    {
+      log_error (_("TOFU DB error"));
+      print_further_info ("dropping ultimately_trusted_keys: %s", err);
+      sqlite3_free (err);
+      goto out;
+    }
+
+  rc = sqlite3_exec (db,
+                     "create table if not exists"
+                     " ultimately_trusted_keys (keyid);\n",
+                     NULL, NULL, &err);
+  if (rc)
+    {
+      log_error (_("TOFU DB error"));
+      print_further_info ("creating ultimately_trusted_keys: %s", err);
+      sqlite3_free (err);
+      goto out;
+    }
+
+  for (ki = utks; ki; ki = ki->next)
+    {
+      format_keyid (ki->kid, KF_LONG,
+                    keyid_str, sizeof (keyid_str));
+      rc = gpgsql_exec_printf
+        (db, NULL, NULL, &err,
+         "insert into ultimately_trusted_keys values ('%s');",
+         keyid_str);
+      if (rc)
+        {
+          log_error (_("TOFU DB error"));
+          print_further_info ("updating ultimately_trusted_keys: %s",
+                              err);
+          sqlite3_free (err);
+          goto out;
+        }
+    }
+
+ out:
+  return rc;
+}
 
 /* If the DB is new, initialize it.  Otherwise, check the DB's
    version.
@@ -727,6 +873,9 @@ initdb (sqlite3 *db)
        }
     }
 
+  if (! rc)
+    rc = check_utks (db);
+
   if (rc)
     {
       rc = sqlite3_exec (db, "rollback;", NULL, NULL, &err);
index edae6ef..51a8f22 100644 (file)
@@ -324,6 +324,13 @@ tdb_keyid_is_utk (u32 *kid)
 
   return 0;
 }
+
+/* Return the list of ultimately trusted keys.  */
+struct key_item *
+tdb_utks (void)
+{
+  return utk_list;
+}
 \f
 /*********************************************
  *********** TrustDB stuff *******************
index 77aa79d..45ecc56 100644 (file)
@@ -117,6 +117,9 @@ void tdb_register_trusted_keyid (u32 *keyid);
 void tdb_register_trusted_key (const char *string);
 /* Returns whether KID is on the list of ultimately trusted keys.  */
 int tdb_keyid_is_utk (u32 *kid);
+/* Return the list of ultimately trusted keys.  The caller must not
+ * modify this list nor must it free the list.  */
+struct key_item *tdb_utks (void);
 void check_trustdb (ctrl_t ctrl);
 void update_trustdb (ctrl_t ctrl);
 int setup_trustdb( int level, const char *dbname );
index 2a04d13..e1fa001 100755 (executable)
 ;; Alice has an ultimately trusted key and she signs Bob's key.  Then
 ;; Bob adds a new user id, "Alice".  TOFU should now detect a
 ;; conflict, because Alice only signed Bob's "Bob" user id.
+;;
+;;
+;; The Alice key:
+;;   pub   rsa2048 2016-10-11 [SC]
+;;         1938C3A0E4674B6C217AC0B987DB2814EC38277E
+;;   uid           [ultimate] Spy Cow <spy@cow.com>
+;;   sub   rsa2048 2016-10-11 [E]
+;;
+;; The Bob key:
+;;
+;;   pub   rsa2048 2016-10-11 [SC]
+;;         DC463A16E42F03240D76E8BA8B48C6BD871C2247
+;;   uid           [  full  ] Spy R. Cow <spy@cow.com>
+;;   uid           [  full  ] Spy R. Cow <spy@cow.de>
+;;   sub   rsa2048 2016-10-11 [E]
 
 (display "Checking UTK sigs...\n")
 (define GPG `(,(tool 'gpg) --no-permission-warning
 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-1.gpg"))))
 (display "<\n")
 
+(checkpolicy KEYA "auto")
+(checkpolicy KEYB "auto")
+
 ;; Import the cross sigs.
 (display "    > Adding cross signatures. ")
 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDA "-2.gpg"))))
 (call-check `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-2.gpg"))))
 (display "<\n")
 
+(checkpolicy KEYA "auto")
+(checkpolicy KEYB "auto")
+
 ;; Make KEYA ultimately trusted.
 (display (string-append "    > Marking " KEYA " as ultimately trusted. "))
 (pipe:do