g10: If the set of UTKs changes, invalidate any changed policies.

* 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.
This commit is contained in:
Neal H. Walfield 2016-11-22 15:05:59 +01:00
parent 5c2db9dedf
commit 44c17bcb00
4 changed files with 180 additions and 0 deletions

View 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);

View 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;
}
/*********************************************
*********** TrustDB stuff *******************

View 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 );

View File

@ -248,6 +248,21 @@
;; 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
@ -279,12 +294,18 @@
(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