1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-01-09 12:54:23 +01:00

gpg: Split tofu's get_trust function into several smaller ones.

* g10/tofu.c (get_trust): Factor code out to ...
(format_conflict_msg_part1): new and to ...
(ask_about_binding): new.
--

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-06-14 12:02:22 +02:00
parent 5ddccf4fc6
commit 1affdf1efc
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B

View File

@ -1602,312 +1602,124 @@ get_policy (tofu_dbs_t dbs, const char *fingerprint, const char *email,
return 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. */
static enum tofu_policy
get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
const char *fingerprint, const char *email,
const char *user_id, int may_ask)
/* Format the first part of a conflict message and return that as a
* malloced string. */
static char *
format_conflict_msg_part1 (int policy, const char *conflict,
const char *fingerprint, const char *email)
{
char *fingerprint_pp;
struct db *db;
enum tofu_policy policy;
char *conflict = NULL;
estream_t fp;
char *binding;
int binding_shown = 0;
char *tmpstr, *text;
binding = xasprintf ("<%s, %s>", fingerprint, email);
fp = es_fopenmem (0, "rw,samethread");
if (!fp)
log_fatal ("error creating memory stream: %s\n",
gpg_strerror (gpg_error_from_syserror()));
if (policy == TOFU_POLICY_NONE)
{
es_fprintf (fp, _("The binding %s is NOT known."), binding);
es_fputs (" ", fp);
binding_shown = 1;
}
else if (policy == TOFU_POLICY_ASK
/* If there the conflict is with itself, then don't
* display this message. */
&& conflict && strcmp (conflict, fingerprint))
{
es_fprintf (fp,
_("The key with fingerprint %s raised a conflict "
"with the binding %s."
" Since this binding's policy was 'auto', it was "
"changed to 'ask'."),
conflict, binding);
es_fputs (" ", fp);
binding_shown = 1;
}
/* TRANSLATORS: The %s%s is replaced by either a fingerprint and a
blank or by two empty strings. */
es_fprintf (fp,
_("Please indicate whether you believe the binding %s%s"
"is legitimate (the key belongs to the stated owner) "
"or a forgery (bad)."),
binding_shown ? "" : binding,
binding_shown ? "" : " ");
es_fputc ('\n', fp);
xfree (binding);
es_fputc (0, fp);
if (es_fclose_snatch (fp, (void **)&tmpstr, NULL))
log_fatal ("error snatching memory stream\n");
text = format_text (tmpstr, 0, 72, 80);
es_free (tmpstr);
return text;
}
/* Ask the user about the binding. There are three ways we could end
* up here:
*
* - This is a new binding and there is a conflict
* (policy == TOFU_POLICY_NONE && bindings_with_this_email_count > 0),
*
* - This is a new binding and opt.tofu_default_policy is set to
* ask. (policy == TOFU_POLICY_NONE && opt.tofu_default_policy ==
* TOFU_POLICY_ASK), or,
*
* - The policy is ask (the user deferred last time) (policy ==
* TOFU_POLICY_ASK).
*/
static void
ask_about_binding (tofu_dbs_t dbs,
struct db *db,
enum tofu_policy *policy,
int *trust_level,
int bindings_with_this_email_count,
strlist_t bindings_with_this_email,
char *conflict,
const char *fingerprint,
const char *email,
const char *user_id)
{
char *sqerr = NULL;
int rc;
char *err = NULL;
strlist_t bindings_with_this_email = NULL;
int bindings_with_this_email_count;
int change_conflicting_to_ask = 0;
int trust_level = TRUST_UNKNOWN;
if (opt.batch)
may_ask = 0;
/* 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);
db = getdb (dbs, email, DB_EMAIL);
if (! db)
return _tofu_GET_TRUST_ERROR;
fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0);
policy = get_policy (dbs, fingerprint, email, &conflict);
if (policy == TOFU_POLICY_AUTO || policy == TOFU_POLICY_NONE)
{ /* 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)
{
if (record_binding (dbs, fingerprint, email, user_id,
TOFU_POLICY_AUTO, 0) != 0)
{
log_error (_("error setting TOFU binding's trust level"
" to %s\n"), "auto");
trust_level = _tofu_GET_TRUST_ERROR;
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 <%s, %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 <%s, %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. */
if (! may_ask)
{
trust_level = TRUST_UNDEFINED;
goto out;
}
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).
*/
/* Look for conflicts. This is needed in all 3 cases.
Get the fingerprints of any bindings that share the email
address. Note: if the binding in question is in the DB, it will
also be returned. Thus, if the result set is empty, then this is
a new binding. */
rc = gpgsql_stepx
(db->db, &db->s.get_trust_bindings_with_this_email,
strings_collect_cb2, &bindings_with_this_email, &err,
"select distinct fingerprint from bindings where email = ?;",
SQLITE_ARG_STRING, email, SQLITE_ARG_END);
if (rc)
{
log_error (_("error reading TOFU database: %s\n"), err);
print_further_info ("listing fingerprints");
sqlite3_free (err);
goto out;
}
bindings_with_this_email_count = strlist_length (bindings_with_this_email);
if (bindings_with_this_email_count == 0
&& opt.tofu_default_policy != TOFU_POLICY_ASK)
/* New binding with no conflict and a concrete default policy.
We've never observed a binding with this email address
(BINDINGS_WITH_THIS_EMAIL_COUNT is 0 and the above query would return
the current binding if it were in the DB) 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 <%s, %s>, no conflict.\n",
email, fingerprint);
if (record_binding (dbs, fingerprint, email, user_id,
TOFU_POLICY_AUTO, 0) != 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);
goto out;
}
if (policy == TOFU_POLICY_NONE)
/* This is a new binding and 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)
/* We can only get here in the third case (no saved policy) and if
there is a conflict. (If the policy was ask (cases #1 and #2)
and we weren't allowed to ask, we'd have already exited). */
{
log_assert (policy == TOFU_POLICY_NONE);
if (record_binding (dbs, fingerprint, email, user_id,
TOFU_POLICY_ASK, 0) != 0)
log_error (_("error setting TOFU binding's trust level to %s\n"),
"ask");
trust_level = TRUST_UNDEFINED;
goto out;
}
/* If we get here, we need to ask the user about the binding. There
are three ways we could end up here:
- This is a new binding and there is a conflict
(policy == TOFU_POLICY_NONE && bindings_with_this_email_count > 0),
- This is a new binding and opt.tofu_default_policy is set to
ask. (policy == TOFU_POLICY_NONE && opt.tofu_default_policy ==
TOFU_POLICY_ASK), or,
- The policy is ask (the user deferred last time) (policy ==
TOFU_POLICY_ASK).
*/
{
int is_conflict =
((policy == TOFU_POLICY_NONE && bindings_with_this_email_count > 0)
|| (policy == TOFU_POLICY_ASK && conflict));
estream_t fp;
strlist_t other_user_ids = NULL;
struct signature_stats *stats = NULL;
struct signature_stats *stats_iter = NULL;
char *prompt;
char *choices;
struct db *db_key;
fp = es_fopenmem (0, "rw,samethread");
if (! fp)
if (!fp)
log_fatal ("error creating memory stream: %s\n",
gpg_strerror (gpg_error_from_syserror()));
/* Format the first part of the message. */
{
estream_t fp1;
char *binding = xasprintf ("<%s, %s>", fingerprint, email);
int binding_shown = 0;
char *tmpstr, *text;
fp1 = es_fopenmem (0, "rw,samethread");
if (!fp1)
log_fatal ("error creating memory stream: %s\n",
gpg_strerror (gpg_error_from_syserror()));
if (policy == TOFU_POLICY_NONE)
{
es_fprintf (fp1, _("The binding %s is NOT known."), binding);
es_fputs (" ", fp1);
binding_shown = 1;
}
else if (policy == TOFU_POLICY_ASK
/* If there the conflict is with itself, then don't
display this message. */
&& conflict && strcmp (conflict, fingerprint) != 0)
{
es_fprintf (fp1,
_("The key with fingerprint %s raised a conflict "
"with the binding %s."
" Since this binding's policy was 'auto', it was "
"changed to 'ask'."),
conflict, binding);
es_fputs (" ", fp1);
binding_shown = 1;
}
/* TRANSLATORS: The %s%s is replaced by either a fingerprint and a
blank or by two empty strings. */
es_fprintf (fp1,
_("Please indicate whether you believe the binding %s%s"
"is legitimate (the key belongs to the stated owner) "
"or a forgery (bad)."),
binding_shown ? "" : binding,
binding_shown ? "" : " ");
es_fputc ('\n', fp1);
xfree (binding);
es_fputc (0, fp1);
if (es_fclose_snatch (fp1, (void **)&tmpstr, NULL))
log_fatal ("error snatching memory stream\n");
text = format_text (tmpstr, 0, 72, 80);
es_free (tmpstr);
char *text = format_conflict_msg_part1 (*policy, conflict,
fingerprint, email);
es_fputs (text, fp);
es_fputc ('\n', fp);
xfree (text);
}
es_fputc ('\n', fp);
/* Find other user ids associated with this key and whether the
bindings are marked as good or bad. */
{
struct db *db_key;
* bindings are marked as good or bad. */
if (opt.tofu_db_format == TOFU_DB_SPLIT)
/* In the split format, we need to search in the fingerprint
DB for all the emails associated with this key, not the
email DB. */
{
/* In the split format, we need to search in the fingerprint DB
* for all the emails associated with this key, not the email DB. */
db_key = getdb (dbs, fingerprint, DB_KEY);
}
else
db_key = db;
@ -1915,17 +1727,16 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
{
rc = gpgsql_stepx
(db_key->db, &db_key->s.get_trust_gather_other_user_ids,
strings_collect_cb2, &other_user_ids, &err,
strings_collect_cb2, &other_user_ids, &sqerr,
opt.tofu_db_format == TOFU_DB_SPLIT
? "select user_id, email from bindings where fingerprint = ?;"
: "select user_id, policy from bindings where fingerprint = ?;",
SQLITE_ARG_STRING, fingerprint, SQLITE_ARG_END);
if (rc)
{
log_error (_("error gathering other user IDs: %s\n"), err);
sqlite3_free (err);
err = NULL;
}
log_error (_("error gathering other user IDs: %s\n"), sqerr);
sqlite3_free (sqerr);
sqerr = NULL;
}
}
@ -1961,20 +1772,20 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
}
/* Find other keys associated with this email address. */
/* XXX: When generating the statistics, do we want the time
/* FIXME: When generating the statistics, do we want the time
embedded in the signature (column 'sig_time') or the time that
we first verified the signature (column 'time'). */
rc = gpgsql_stepx
(db->db, &db->s.get_trust_gather_other_keys,
signature_stats_collect_cb, &stats, &err,
signature_stats_collect_cb, &stats, &sqerr,
"select fingerprint, policy, time_ago, count(*)\n"
" from (select bindings.*,\n"
" case\n"
/* From the future (but if its just a couple of hours in the
future don't turn it into a warning)? Or should we use
small, medium or large units? (Note: whatever we do, we
keep the value in seconds. Then when we group, everything
that rounds to the same number of seconds is grouped.) */
* future don't turn it into a warning)? Or should we use
* small, medium or large units? (Note: whatever we do, we
* keep the value in seconds. Then when we group, everything
* that rounds to the same number of seconds is grouped.) */
" when delta < -("STRINGIFY (TIME_AGO_FUTURE_IGNORE)") then -1\n"
" when delta < ("STRINGIFY (TIME_AGO_MEDIUM_THRESHOLD)")\n"
" then max(0,\n"
@ -2003,9 +1814,9 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
{
strlist_t strlist_iter;
log_error (_("error gathering signature stats: %s\n"), err);
sqlite3_free (err);
err = NULL;
log_error (_("error gathering signature stats: %s\n"), sqerr);
sqlite3_free (sqerr);
sqerr = NULL;
es_fprintf (fp, ngettext("The email address \"%s\" is"
" associated with %d key:\n",
@ -2022,22 +1833,24 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
{
char *key = NULL;
if (! stats || strcmp (stats->fingerprint, fingerprint) != 0)
if (! stats || strcmp (stats->fingerprint, fingerprint))
{
/* If we have already added this key to the DB, then it will
be first (see the above select). Since the first key on
the list is not this key, we must not yet have verified
any messages signed by this key. Add a dummy entry. */
* be first (see the above select). Since the first key on
* the list is not this key, we must not yet have verified any
* messages signed by this key. Add a dummy entry. */
signature_stats_prepend (&stats, fingerprint, TOFU_POLICY_AUTO, 0, 0);
}
es_fprintf
(fp, _("Statistics for keys with the email address \"%s\":\n"),
es_fprintf (fp, _("Statistics for keys with the email address \"%s\":\n"),
email);
for (stats_iter = stats; stats_iter; stats_iter = stats_iter->next)
{
if (! key || strcmp (key, stats_iter->fingerprint) != 0)
if (! key || strcmp (key, stats_iter->fingerprint))
{
int this_key;
char *key_pp;
key = stats_iter->fingerprint;
this_key = strcmp (key, fingerprint) == 0;
key_pp = format_hexfingerprint (key, NULL, 0);
@ -2084,14 +1897,19 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
}
}
if (is_conflict)
if ((*policy == TOFU_POLICY_NONE && bindings_with_this_email_count > 0)
|| (*policy == TOFU_POLICY_ASK && conflict))
{
/* This is a conflict. */
/* TRANSLATORS: Please translate the text found in the source
file below. We don't directly internationalize that text
so that we can tweak it without breaking translations. */
* file below. We don't directly internationalize that text so
* that we can tweak it without breaking translations. */
char *text = _("TOFU detected a binding conflict");
char *textbuf;
if (strcmp (text, "TOFU detected a binding conflict") == 0)
if (!strcmp (text, "TOFU detected a binding conflict"))
{
/* No translation. Use the English text. */
text =
"Normally, there is only a single key associated with an email "
@ -2100,6 +1918,7 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
"Alternatively, a new key may indicate a man-in-the-middle "
"attack! Before accepting this key, you should talk to or "
"call the person to make sure this new key is legitimate.";
}
textbuf = format_text (text, 0, 72, 80);
es_fprintf (fp, "\n%s\n", text);
xfree (textbuf);
@ -2113,18 +1932,18 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
log_fatal ("error snatching memory stream\n");
/* I think showing the large message once is sufficient. If we
would move it right before the cpr_get many lines will scroll
away and the user might not realize that he merely entered a
wrong choise (because he does not see that either). As a small
benefit we allow C-L to redisplay everything. */
* would move it right before the cpr_get many lines will scroll
* away and the user might not realize that he merely entered a
* wrong choise (because he does not see that either). As a small
* benefit we allow C-L to redisplay everything. */
tty_printf ("%s", prompt);
while (1)
{
char *response;
/* TRANSLATORS: Two letters (normally the lower and upper case
version of the hotkey) for each of the five choices. If
there is only one choice in your language, repeat it. */
* version of the hotkey) for each of the five choices. If
* there is only one choice in your language, repeat it. */
choices = _("gG" "aA" "uU" "rR" "bB");
if (strlen (choices) != 10)
log_bug ("Bad TOFU conflict translation! Please report.");
@ -2146,37 +1965,36 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
switch (c)
{
case 0: /* Good. */
policy = TOFU_POLICY_GOOD;
trust_level = tofu_policy_to_trust_level (policy);
*policy = TOFU_POLICY_GOOD;
*trust_level = tofu_policy_to_trust_level (*policy);
break;
case 1: /* Accept once. */
policy = TOFU_POLICY_ASK;
trust_level =
tofu_policy_to_trust_level (TOFU_POLICY_GOOD);
*policy = TOFU_POLICY_ASK;
*trust_level = tofu_policy_to_trust_level (TOFU_POLICY_GOOD);
break;
case 2: /* Unknown. */
policy = TOFU_POLICY_UNKNOWN;
trust_level = tofu_policy_to_trust_level (policy);
*policy = TOFU_POLICY_UNKNOWN;
*trust_level = tofu_policy_to_trust_level (*policy);
break;
case 3: /* Reject once. */
policy = TOFU_POLICY_ASK;
trust_level =
tofu_policy_to_trust_level (TOFU_POLICY_BAD);
*policy = TOFU_POLICY_ASK;
*trust_level = tofu_policy_to_trust_level (TOFU_POLICY_BAD);
break;
case 4: /* Bad. */
policy = TOFU_POLICY_BAD;
trust_level = tofu_policy_to_trust_level (policy);
*policy = TOFU_POLICY_BAD;
*trust_level = tofu_policy_to_trust_level (*policy);
break;
default:
log_bug ("c should be between 0 and 4 but it is %d!", c);
}
if (record_binding (dbs, fingerprint, email, user_id,
policy, 0) != 0)
*policy, 0))
{
/* If there's an error registering the
binding, don't save the signature. */
trust_level = _tofu_GET_TRUST_ERROR;
* binding, don't save the signature. */
*trust_level = _tofu_GET_TRUST_ERROR;
}
break;
}
}
@ -2186,38 +2004,266 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
xfree (prompt);
signature_stats_free (stats);
}
/* 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. */
static enum tofu_policy
get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
const char *fingerprint, const char *email,
const char *user_id, int may_ask)
{
struct db *db;
enum tofu_policy policy;
char *conflict = NULL;
int rc;
char *sqerr = NULL;
strlist_t bindings_with_this_email = NULL;
int bindings_with_this_email_count;
int change_conflicting_to_ask = 0;
int trust_level = TRUST_UNKNOWN;
if (opt.batch)
may_ask = 0;
/* 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);
db = getdb (dbs, email, DB_EMAIL);
if (! db)
return _tofu_GET_TRUST_ERROR;
policy = get_policy (dbs, fingerprint, email, &conflict);
if (policy == TOFU_POLICY_AUTO || policy == TOFU_POLICY_NONE)
{ /* 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)
{
if (record_binding (dbs, fingerprint, email, user_id,
TOFU_POLICY_AUTO, 0) != 0)
{
log_error (_("error setting TOFU binding's trust level"
" to %s\n"), "auto");
trust_level = _tofu_GET_TRUST_ERROR;
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 <%s, %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 <%s, %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. */
if (! may_ask)
{
trust_level = TRUST_UNDEFINED;
goto out;
}
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).
*/
/* Look for conflicts. This is needed in all 3 cases.
*
* Get the fingerprints of any bindings that share the email
* address. Note: if the binding in question is in the DB, it will
* also be returned. Thus, if the result set is empty, then this is
* a new binding. */
rc = gpgsql_stepx
(db->db, &db->s.get_trust_bindings_with_this_email,
strings_collect_cb2, &bindings_with_this_email, &sqerr,
"select distinct fingerprint from bindings where email = ?;",
SQLITE_ARG_STRING, email, SQLITE_ARG_END);
if (rc)
{
log_error (_("error reading TOFU database: %s\n"), sqerr);
print_further_info ("listing fingerprints");
sqlite3_free (sqerr);
goto out;
}
bindings_with_this_email_count = strlist_length (bindings_with_this_email);
if (bindings_with_this_email_count == 0
&& opt.tofu_default_policy != TOFU_POLICY_ASK)
{
/* New binding with no conflict and a concrete default policy.
*
* We've never observed a binding with this email address
* BINDINGS_WITH_THIS_EMAIL_COUNT is 0 and the above query would
* return the current binding if it were in the DB) 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 <%s, %s>, no conflict.\n",
email, fingerprint);
if (record_binding (dbs, fingerprint, email, user_id,
TOFU_POLICY_AUTO, 0) != 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);
goto out;
}
if (policy == TOFU_POLICY_NONE)
{
/* This is a new binding and 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)
{
/* We can only get here in the third case (no saved policy) and
* if there is a conflict. (If the policy was ask (cases #1 and
* #2) and we weren't allowed to ask, we'd have already exited). */
log_assert (policy == TOFU_POLICY_NONE);
if (record_binding (dbs, fingerprint, email, user_id,
TOFU_POLICY_ASK, 0) != 0)
log_error (_("error setting TOFU binding's trust level to %s\n"),
"ask");
trust_level = TRUST_UNDEFINED;
goto out;
}
/* If we get here, we need to ask the user about the binding. */
ask_about_binding (dbs, db,
&policy,
&trust_level,
bindings_with_this_email_count,
bindings_with_this_email,
conflict,
fingerprint,
email,
user_id);
out:
if (change_conflicting_to_ask)
{
if (! may_ask)
{
/* If we weren't allowed to ask, also update this key as
conflicting with itself. */
rc = gpgsql_exec_printf
(db->db, NULL, NULL, &err,
(db->db, NULL, NULL, &sqerr,
"update bindings set policy = %d, conflict = %Q"
" where email = %Q"
" and (policy = %d or (policy = %d and fingerprint = %Q));",
TOFU_POLICY_ASK, fingerprint, email, TOFU_POLICY_AUTO,
TOFU_POLICY_ASK, fingerprint);
}
else
{
rc = gpgsql_exec_printf
(db->db, NULL, NULL, &err,
(db->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, fingerprint, TOFU_POLICY_AUTO);
TOFU_POLICY_ASK, fingerprint, email, fingerprint,
TOFU_POLICY_AUTO);
}
if (rc)
{
log_error (_("error changing TOFU policy: %s\n"), err);
sqlite3_free (err);
goto out;
log_error (_("error changing TOFU policy: %s\n"), sqerr);
sqlite3_free (sqerr);
goto out; /* FIXME */
}
}
xfree (conflict);
free_strlist (bindings_with_this_email);
xfree (fingerprint_pp);
return trust_level;
}