mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
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@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.
This commit is contained in:
parent
182efc5b5d
commit
037f9de092
@ -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;
|
||||
}
|
||||
|
11
g10/import.c
11
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) );
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
790
g10/tofu.c
790
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,
|
||||
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,152 +2099,162 @@ build_conflict_set (tofu_dbs_t dbs,
|
||||
}
|
||||
|
||||
|
||||
/* 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,
|
||||
* returns _tofu_GET_TRUST_ERROR.
|
||||
*
|
||||
* PK is the public key object for FINGERPRINT.
|
||||
*
|
||||
* USER_ID is the unadulterated user id.
|
||||
*
|
||||
* If MAY_ASK is set, then we may interact with the user. This is
|
||||
* necessary if there is a conflict or the binding's policy is
|
||||
* TOFU_POLICY_ASK. In the case of a conflict, we set the new
|
||||
* conflicting binding's policy to TOFU_POLICY_ASK. In either case,
|
||||
* we return TRUST_UNDEFINED. Note: if MAY_ASK is set, then this
|
||||
* function must not be called while in a transaction! */
|
||||
/* 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_trust (ctrl_t ctrl, PKT_public_key *pk,
|
||||
const char *fingerprint, const char *email,
|
||||
const char *user_id, int may_ask, time_t now)
|
||||
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)
|
||||
{
|
||||
tofu_dbs_t dbs = ctrl->tofu.dbs;
|
||||
int in_transaction = 0;
|
||||
enum tofu_policy policy;
|
||||
int rc;
|
||||
char *sqerr = NULL;
|
||||
int change_conflicting_to_ask = 0;
|
||||
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;
|
||||
int trust_level = TRUST_UNKNOWN;
|
||||
strlist_t iter;
|
||||
|
||||
log_assert (dbs);
|
||||
|
||||
if (may_ask)
|
||||
log_assert (dbs->in_transaction == 0);
|
||||
|
||||
if (opt.batch)
|
||||
may_ask = 0;
|
||||
|
||||
log_assert (pk_is_primary (pk));
|
||||
|
||||
/* Make sure _tofu_GET_TRUST_ERROR isn't equal to any of the trust
|
||||
levels. */
|
||||
log_assert (_tofu_GET_TRUST_ERROR != TRUST_UNKNOWN
|
||||
&& _tofu_GET_TRUST_ERROR != TRUST_EXPIRED
|
||||
&& _tofu_GET_TRUST_ERROR != TRUST_UNDEFINED
|
||||
&& _tofu_GET_TRUST_ERROR != TRUST_NEVER
|
||||
&& _tofu_GET_TRUST_ERROR != TRUST_MARGINAL
|
||||
&& _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);
|
||||
/* 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. */
|
||||
{
|
||||
/* 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;
|
||||
effective_policy = TOFU_POLICY_GOOD;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
trust_level = TRUST_ULTIMATE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (policy == TOFU_POLICY_AUTO)
|
||||
{
|
||||
policy = opt.tofu_default_policy;
|
||||
if (DBG_TRUST)
|
||||
log_debug ("TOFU: binding <key: %s, user id: %s>'s policy is"
|
||||
" auto (default: %s).\n",
|
||||
fingerprint, email,
|
||||
tofu_policy_str (opt.tofu_default_policy));
|
||||
}
|
||||
switch (policy)
|
||||
{
|
||||
case TOFU_POLICY_AUTO:
|
||||
case TOFU_POLICY_GOOD:
|
||||
case TOFU_POLICY_UNKNOWN:
|
||||
case TOFU_POLICY_BAD:
|
||||
/* The saved judgement is auto -> auto, good, unknown or bad.
|
||||
* We don't need to ask the user anything. */
|
||||
if (DBG_TRUST)
|
||||
log_debug ("TOFU: Known binding <key: %s, user id: %s>'s policy: %s\n",
|
||||
fingerprint, email, tofu_policy_str (policy));
|
||||
trust_level = tofu_policy_to_trust_level (policy);
|
||||
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. */
|
||||
break;
|
||||
|
||||
case _tofu_GET_POLICY_ERROR:
|
||||
trust_level = _tofu_GET_TRUST_ERROR;
|
||||
goto out;
|
||||
|
||||
default:
|
||||
log_bug ("%s: Impossible value for policy (%d)\n", __func__, policy);
|
||||
}
|
||||
|
||||
|
||||
/* We get here if:
|
||||
*
|
||||
* 1. The saved policy is auto and the default policy is ask
|
||||
* (get_policy() == TOFU_POLICY_AUTO
|
||||
* && opt.tofu_default_policy == TOFU_POLICY_ASK)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Before continuing, see if the key is signed by an ultimately
|
||||
* trusted key. */
|
||||
/* 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,
|
||||
@ -2322,41 +2281,33 @@ get_trust (ctrl_t ctrl, PKT_public_key *pk,
|
||||
}
|
||||
else
|
||||
{
|
||||
is_signed_by_utk = signed_by_utk (email, kb);
|
||||
int 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;
|
||||
|
||||
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. */
|
||||
|
||||
/* 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;
|
||||
/* 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)
|
||||
&& opt.tofu_default_policy != TOFU_POLICY_ASK)
|
||||
&& (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. */
|
||||
@ -2369,70 +2320,223 @@ get_trust (ctrl_t ctrl, PKT_public_key *pk,
|
||||
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;
|
||||
}
|
||||
|
||||
trust_level = tofu_policy_to_trust_level (TOFU_POLICY_AUTO);
|
||||
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. 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);
|
||||
/* 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);
|
||||
|
||||
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");
|
||||
effective_policy = TOFU_POLICY_AUTO;
|
||||
conflict = NULL;
|
||||
|
||||
trust_level = tofu_policy_to_trust_level (TOFU_POLICY_AUTO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
if (! may_ask)
|
||||
if (conflict_set_count == 1)
|
||||
{
|
||||
log_assert (policy == TOFU_POLICY_NONE || policy == TOFU_POLICY_ASK);
|
||||
if (policy == TOFU_POLICY_NONE)
|
||||
{
|
||||
/* 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");
|
||||
}
|
||||
/* No conflicts and never marked as conflicting. */
|
||||
|
||||
log_assert (!conflict);
|
||||
|
||||
effective_policy = TOFU_POLICY_AUTO;
|
||||
|
||||
trust_level = TRUST_UNDEFINED;
|
||||
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,
|
||||
* returns _tofu_GET_TRUST_ERROR.
|
||||
*
|
||||
* PK is the public key object for FINGERPRINT.
|
||||
*
|
||||
* USER_ID is the unadulterated user id.
|
||||
*
|
||||
* If MAY_ASK is set, then we may interact with the user. This is
|
||||
* necessary if there is a conflict or the binding's policy is
|
||||
* TOFU_POLICY_ASK. In the case of a conflict, we set the new
|
||||
* conflicting binding's policy to TOFU_POLICY_ASK. In either case,
|
||||
* we return TRUST_UNDEFINED. Note: if MAY_ASK is set, then this
|
||||
* function must not be called while in a transaction! */
|
||||
static enum tofu_policy
|
||||
get_trust (ctrl_t ctrl, PKT_public_key *pk,
|
||||
const char *fingerprint, const char *email,
|
||||
const char *user_id, int may_ask, time_t now)
|
||||
{
|
||||
tofu_dbs_t dbs = ctrl->tofu.dbs;
|
||||
int in_transaction = 0;
|
||||
enum tofu_policy policy;
|
||||
int rc;
|
||||
char *sqerr = NULL;
|
||||
strlist_t conflict_set = NULL;
|
||||
int trust_level = TRUST_UNKNOWN;
|
||||
strlist_t iter;
|
||||
|
||||
log_assert (dbs);
|
||||
|
||||
if (may_ask)
|
||||
log_assert (dbs->in_transaction == 0);
|
||||
|
||||
if (opt.batch)
|
||||
may_ask = 0;
|
||||
|
||||
log_assert (pk_is_primary (pk));
|
||||
|
||||
/* Make sure _tofu_GET_TRUST_ERROR isn't equal to any of the trust
|
||||
levels. */
|
||||
log_assert (_tofu_GET_TRUST_ERROR != TRUST_UNKNOWN
|
||||
&& _tofu_GET_TRUST_ERROR != TRUST_EXPIRED
|
||||
&& _tofu_GET_TRUST_ERROR != TRUST_UNDEFINED
|
||||
&& _tofu_GET_TRUST_ERROR != TRUST_NEVER
|
||||
&& _tofu_GET_TRUST_ERROR != TRUST_MARGINAL
|
||||
&& _tofu_GET_TRUST_ERROR != TRUST_FULLY
|
||||
&& _tofu_GET_TRUST_ERROR != TRUST_ULTIMATE);
|
||||
|
||||
/* If the key is ultimately trusted, there is nothing to do. */
|
||||
{
|
||||
u32 kid[2];
|
||||
|
||||
keyid_from_pk (pk, kid);
|
||||
if (tdb_keyid_is_utk (kid))
|
||||
{
|
||||
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;
|
||||
if (DBG_TRUST)
|
||||
log_debug ("TOFU: binding <key: %s, user id: %s>'s policy is"
|
||||
" auto (default: %s).\n",
|
||||
fingerprint, email,
|
||||
tofu_policy_str (opt.tofu_default_policy));
|
||||
}
|
||||
switch (policy)
|
||||
{
|
||||
case TOFU_POLICY_AUTO:
|
||||
case TOFU_POLICY_GOOD:
|
||||
case TOFU_POLICY_UNKNOWN:
|
||||
case TOFU_POLICY_BAD:
|
||||
/* The saved judgement is auto -> auto, good, unknown or bad.
|
||||
* We don't need to ask the user anything. */
|
||||
if (DBG_TRUST)
|
||||
log_debug ("TOFU: Known binding <key: %s, user id: %s>'s policy: %s\n",
|
||||
fingerprint, email, tofu_policy_str (policy));
|
||||
trust_level = tofu_policy_to_trust_level (policy);
|
||||
goto out;
|
||||
|
||||
case TOFU_POLICY_ASK:
|
||||
/* We need to ask the user what to do. */
|
||||
break;
|
||||
|
||||
case _tofu_GET_POLICY_ERROR:
|
||||
trust_level = _tofu_GET_TRUST_ERROR;
|
||||
goto out;
|
||||
|
||||
default:
|
||||
log_bug ("%s: Impossible value for policy (%d)\n", __func__, policy);
|
||||
}
|
||||
|
||||
|
||||
/* We get here if:
|
||||
*
|
||||
* 1. The saved policy is auto and the default policy is ask
|
||||
* (get_policy() == TOFU_POLICY_AUTO
|
||||
* && opt.tofu_default_policy == TOFU_POLICY_ASK)
|
||||
*
|
||||
* 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).
|
||||
*/
|
||||
log_assert (policy == TOFU_POLICY_ASK);
|
||||
|
||||
if (may_ask)
|
||||
{
|
||||
/* We can't be in a normal transaction in ask_about_binding. */
|
||||
end_transaction (ctrl, 0);
|
||||
in_transaction = 0;
|
||||
@ -2446,31 +2550,34 @@ get_trust (ctrl_t ctrl, PKT_public_key *pk,
|
||||
email,
|
||||
user_id,
|
||||
now);
|
||||
}
|
||||
else
|
||||
trust_level = TRUST_UNDEFINED;
|
||||
|
||||
out:
|
||||
|
||||
if (change_conflicting_to_ask)
|
||||
{
|
||||
/* Mark any conflicting bindings that have an automatic policy as
|
||||
* now requiring confirmation. */
|
||||
|
||||
* 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)
|
||||
{
|
||||
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)
|
||||
/* The conflict set should always contain at least one element:
|
||||
* the current key. */
|
||||
log_assert (conflict_set);
|
||||
|
||||
for (iter = conflict_set->next; iter; iter = iter->next)
|
||||
{
|
||||
/* We don't immediately set the effective policy to 'ask,
|
||||
because */
|
||||
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);
|
||||
"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)
|
||||
{
|
||||
log_error (_("error changing TOFU policy: %s\n"), sqerr);
|
||||
@ -2484,8 +2591,8 @@ get_trust (ctrl_t ctrl, PKT_public_key *pk,
|
||||
log_debug ("Set %s to conflict with %s\n",
|
||||
iter->d, fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
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;
|
||||
}
|
||||
|
@ -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*/
|
||||
|
@ -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 `(,@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 `(,@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 `(,@GPG --import ,(in-srcdir DIR (string-append KEYIDB "-4.gpg"))))
|
||||
(verify-messages)
|
||||
|
Loading…
x
Reference in New Issue
Block a user