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:
parent
5ddccf4fc6
commit
1affdf1efc
724
g10/tofu.c
724
g10/tofu.c
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user