1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-01-24 15:17:02 +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 policy;
} }
/* Return the trust level (TRUST_NEVER, etc.) for the binding
* <FINGERPRINT, EMAIL> (email is already normalized). If no policy /* Format the first part of a conflict message and return that as a
* is registered, returns TOFU_POLICY_NONE. If an error occurs, * malloced string. */
* returns _tofu_GET_TRUST_ERROR. static char *
* format_conflict_msg_part1 (int policy, const char *conflict,
* PK is the public key object for FINGERPRINT. const char *fingerprint, const char *email)
*
* 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)
{ {
char *fingerprint_pp; estream_t fp;
struct db *db; char *binding;
enum tofu_policy policy; int binding_shown = 0;
char *conflict = NULL; 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; 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; estream_t fp;
strlist_t other_user_ids = NULL; strlist_t other_user_ids = NULL;
struct signature_stats *stats = NULL; struct signature_stats *stats = NULL;
struct signature_stats *stats_iter = NULL; struct signature_stats *stats_iter = NULL;
char *prompt; char *prompt;
char *choices; char *choices;
struct db *db_key;
fp = es_fopenmem (0, "rw,samethread"); fp = es_fopenmem (0, "rw,samethread");
if (! fp) if (!fp)
log_fatal ("error creating memory stream: %s\n", log_fatal ("error creating memory stream: %s\n",
gpg_strerror (gpg_error_from_syserror())); gpg_strerror (gpg_error_from_syserror()));
/* Format the first part of the message. */
{ {
estream_t fp1; char *text = format_conflict_msg_part1 (*policy, conflict,
char *binding = xasprintf ("<%s, %s>", fingerprint, email); 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);
es_fputs (text, fp); es_fputs (text, fp);
es_fputc ('\n', fp);
xfree (text); xfree (text);
} }
es_fputc ('\n', fp);
/* Find other user ids associated with this key and whether the /* Find other user ids associated with this key and whether the
bindings are marked as good or bad. */ * bindings are marked as good or bad. */
{
struct db *db_key;
if (opt.tofu_db_format == TOFU_DB_SPLIT) 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 /* In the split format, we need to search in the fingerprint DB
email DB. */ * for all the emails associated with this key, not the email DB. */
db_key = getdb (dbs, fingerprint, DB_KEY); db_key = getdb (dbs, fingerprint, DB_KEY);
}
else else
db_key = db; db_key = db;
@ -1915,17 +1727,16 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
{ {
rc = gpgsql_stepx rc = gpgsql_stepx
(db_key->db, &db_key->s.get_trust_gather_other_user_ids, (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 opt.tofu_db_format == TOFU_DB_SPLIT
? "select user_id, email from bindings where fingerprint = ?;" ? "select user_id, email from bindings where fingerprint = ?;"
: "select user_id, policy from bindings where fingerprint = ?;", : "select user_id, policy from bindings where fingerprint = ?;",
SQLITE_ARG_STRING, fingerprint, SQLITE_ARG_END); SQLITE_ARG_STRING, fingerprint, SQLITE_ARG_END);
if (rc) if (rc)
{ {
log_error (_("error gathering other user IDs: %s\n"), err); log_error (_("error gathering other user IDs: %s\n"), sqerr);
sqlite3_free (err); sqlite3_free (sqerr);
err = NULL; sqerr = NULL;
}
} }
} }
@ -1961,20 +1772,20 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
} }
/* Find other keys associated with this email address. */ /* 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 embedded in the signature (column 'sig_time') or the time that
we first verified the signature (column 'time'). */ we first verified the signature (column 'time'). */
rc = gpgsql_stepx rc = gpgsql_stepx
(db->db, &db->s.get_trust_gather_other_keys, (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" "select fingerprint, policy, time_ago, count(*)\n"
" from (select bindings.*,\n" " from (select bindings.*,\n"
" case\n" " case\n"
/* From the future (but if its just a couple of hours in the /* 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 * future don't turn it into a warning)? Or should we use
small, medium or large units? (Note: whatever we do, we * small, medium or large units? (Note: whatever we do, we
keep the value in seconds. Then when we group, everything * keep the value in seconds. Then when we group, everything
that rounds to the same number of seconds is grouped.) */ * 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_FUTURE_IGNORE)") then -1\n"
" when delta < ("STRINGIFY (TIME_AGO_MEDIUM_THRESHOLD)")\n" " when delta < ("STRINGIFY (TIME_AGO_MEDIUM_THRESHOLD)")\n"
" then max(0,\n" " then max(0,\n"
@ -2003,9 +1814,9 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
{ {
strlist_t strlist_iter; strlist_t strlist_iter;
log_error (_("error gathering signature stats: %s\n"), err); log_error (_("error gathering signature stats: %s\n"), sqerr);
sqlite3_free (err); sqlite3_free (sqerr);
err = NULL; sqerr = NULL;
es_fprintf (fp, ngettext("The email address \"%s\" is" es_fprintf (fp, ngettext("The email address \"%s\" is"
" associated with %d key:\n", " associated with %d key:\n",
@ -2022,22 +1833,24 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
{ {
char *key = NULL; 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 /* If we have already added this key to the DB, then it will
be first (see the above select). Since the first key on * be first (see the above select). Since the first key on
the list is not this key, we must not yet have verified * the list is not this key, we must not yet have verified any
any messages signed by this key. Add a dummy entry. */ * messages signed by this key. Add a dummy entry. */
signature_stats_prepend (&stats, fingerprint, TOFU_POLICY_AUTO, 0, 0); signature_stats_prepend (&stats, fingerprint, TOFU_POLICY_AUTO, 0, 0);
}
es_fprintf es_fprintf (fp, _("Statistics for keys with the email address \"%s\":\n"),
(fp, _("Statistics for keys with the email address \"%s\":\n"),
email); email);
for (stats_iter = stats; stats_iter; stats_iter = stats_iter->next) 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; int this_key;
char *key_pp; char *key_pp;
key = stats_iter->fingerprint; key = stats_iter->fingerprint;
this_key = strcmp (key, fingerprint) == 0; this_key = strcmp (key, fingerprint) == 0;
key_pp = format_hexfingerprint (key, NULL, 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 /* TRANSLATORS: Please translate the text found in the source
file below. We don't directly internationalize that text * file below. We don't directly internationalize that text so
so that we can tweak it without breaking translations. */ * that we can tweak it without breaking translations. */
char *text = _("TOFU detected a binding conflict"); char *text = _("TOFU detected a binding conflict");
char *textbuf; 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. */ /* No translation. Use the English text. */
text = text =
"Normally, there is only a single key associated with an email " "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 " "Alternatively, a new key may indicate a man-in-the-middle "
"attack! Before accepting this key, you should talk to or " "attack! Before accepting this key, you should talk to or "
"call the person to make sure this new key is legitimate."; "call the person to make sure this new key is legitimate.";
}
textbuf = format_text (text, 0, 72, 80); textbuf = format_text (text, 0, 72, 80);
es_fprintf (fp, "\n%s\n", text); es_fprintf (fp, "\n%s\n", text);
xfree (textbuf); xfree (textbuf);
@ -2113,18 +1932,18 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
log_fatal ("error snatching memory stream\n"); log_fatal ("error snatching memory stream\n");
/* I think showing the large message once is sufficient. If we /* I think showing the large message once is sufficient. If we
would move it right before the cpr_get many lines will scroll * would move it right before the cpr_get many lines will scroll
away and the user might not realize that he merely entered a * away and the user might not realize that he merely entered a
wrong choise (because he does not see that either). As a small * wrong choise (because he does not see that either). As a small
benefit we allow C-L to redisplay everything. */ * benefit we allow C-L to redisplay everything. */
tty_printf ("%s", prompt); tty_printf ("%s", prompt);
while (1) while (1)
{ {
char *response; char *response;
/* TRANSLATORS: Two letters (normally the lower and upper case /* TRANSLATORS: Two letters (normally the lower and upper case
version of the hotkey) for each of the five choices. If * version of the hotkey) for each of the five choices. If
there is only one choice in your language, repeat it. */ * there is only one choice in your language, repeat it. */
choices = _("gG" "aA" "uU" "rR" "bB"); choices = _("gG" "aA" "uU" "rR" "bB");
if (strlen (choices) != 10) if (strlen (choices) != 10)
log_bug ("Bad TOFU conflict translation! Please report."); log_bug ("Bad TOFU conflict translation! Please report.");
@ -2146,37 +1965,36 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
switch (c) switch (c)
{ {
case 0: /* Good. */ case 0: /* Good. */
policy = TOFU_POLICY_GOOD; *policy = TOFU_POLICY_GOOD;
trust_level = tofu_policy_to_trust_level (policy); *trust_level = tofu_policy_to_trust_level (*policy);
break; break;
case 1: /* Accept once. */ case 1: /* Accept once. */
policy = TOFU_POLICY_ASK; *policy = TOFU_POLICY_ASK;
trust_level = *trust_level = tofu_policy_to_trust_level (TOFU_POLICY_GOOD);
tofu_policy_to_trust_level (TOFU_POLICY_GOOD);
break; break;
case 2: /* Unknown. */ case 2: /* Unknown. */
policy = TOFU_POLICY_UNKNOWN; *policy = TOFU_POLICY_UNKNOWN;
trust_level = tofu_policy_to_trust_level (policy); *trust_level = tofu_policy_to_trust_level (*policy);
break; break;
case 3: /* Reject once. */ case 3: /* Reject once. */
policy = TOFU_POLICY_ASK; *policy = TOFU_POLICY_ASK;
trust_level = *trust_level = tofu_policy_to_trust_level (TOFU_POLICY_BAD);
tofu_policy_to_trust_level (TOFU_POLICY_BAD);
break; break;
case 4: /* Bad. */ case 4: /* Bad. */
policy = TOFU_POLICY_BAD; *policy = TOFU_POLICY_BAD;
trust_level = tofu_policy_to_trust_level (policy); *trust_level = tofu_policy_to_trust_level (*policy);
break; break;
default: default:
log_bug ("c should be between 0 and 4 but it is %d!", c); log_bug ("c should be between 0 and 4 but it is %d!", c);
} }
if (record_binding (dbs, fingerprint, email, user_id, if (record_binding (dbs, fingerprint, email, user_id,
policy, 0) != 0) *policy, 0))
{
/* If there's an error registering the /* If there's an error registering the
binding, don't save the signature. */ * binding, don't save the signature. */
trust_level = _tofu_GET_TRUST_ERROR; *trust_level = _tofu_GET_TRUST_ERROR;
}
break; break;
} }
} }
@ -2186,38 +2004,266 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
xfree (prompt); xfree (prompt);
signature_stats_free (stats); 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: out:
if (change_conflicting_to_ask) if (change_conflicting_to_ask)
{ {
if (! may_ask) if (! may_ask)
{
/* If we weren't allowed to ask, also update this key as /* If we weren't allowed to ask, also update this key as
conflicting with itself. */ conflicting with itself. */
rc = gpgsql_exec_printf rc = gpgsql_exec_printf
(db->db, NULL, NULL, &err, (db->db, NULL, NULL, &sqerr,
"update bindings set policy = %d, conflict = %Q" "update bindings set policy = %d, conflict = %Q"
" where email = %Q" " where email = %Q"
" and (policy = %d or (policy = %d and fingerprint = %Q));", " and (policy = %d or (policy = %d and fingerprint = %Q));",
TOFU_POLICY_ASK, fingerprint, email, TOFU_POLICY_AUTO, TOFU_POLICY_ASK, fingerprint, email, TOFU_POLICY_AUTO,
TOFU_POLICY_ASK, fingerprint); TOFU_POLICY_ASK, fingerprint);
}
else else
{
rc = gpgsql_exec_printf rc = gpgsql_exec_printf
(db->db, NULL, NULL, &err, (db->db, NULL, NULL, &sqerr,
"update bindings set policy = %d, conflict = %Q" "update bindings set policy = %d, conflict = %Q"
" where email = %Q and fingerprint != %Q and policy = %d;", " 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) if (rc)
{ {
log_error (_("error changing TOFU policy: %s\n"), err); log_error (_("error changing TOFU policy: %s\n"), sqerr);
sqlite3_free (err); sqlite3_free (sqerr);
goto out; goto out; /* FIXME */
} }
} }
xfree (conflict); xfree (conflict);
free_strlist (bindings_with_this_email); free_strlist (bindings_with_this_email);
xfree (fingerprint_pp);
return trust_level; return trust_level;
} }