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
914
g10/tofu.c
914
g10/tofu.c
@ -1602,6 +1602,411 @@ get_policy (tofu_dbs_t dbs, const char *fingerprint, const char *email,
|
|||||||
return policy;
|
return policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
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)
|
||||||
|
log_fatal ("error creating memory stream: %s\n",
|
||||||
|
gpg_strerror (gpg_error_from_syserror()));
|
||||||
|
|
||||||
|
{
|
||||||
|
char *text = format_conflict_msg_part1 (*policy, conflict,
|
||||||
|
fingerprint, email);
|
||||||
|
es_fputs (text, fp);
|
||||||
|
es_fputc ('\n', fp);
|
||||||
|
xfree (text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find other user ids associated with this key and whether the
|
||||||
|
* 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. */
|
||||||
|
db_key = getdb (dbs, fingerprint, DB_KEY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
db_key = db;
|
||||||
|
|
||||||
|
if (db_key)
|
||||||
|
{
|
||||||
|
rc = gpgsql_stepx
|
||||||
|
(db_key->db, &db_key->s.get_trust_gather_other_user_ids,
|
||||||
|
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"), sqerr);
|
||||||
|
sqlite3_free (sqerr);
|
||||||
|
sqerr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other_user_ids)
|
||||||
|
{
|
||||||
|
strlist_t strlist_iter;
|
||||||
|
|
||||||
|
es_fprintf (fp, _("Known user IDs associated with this key:\n"));
|
||||||
|
for (strlist_iter = other_user_ids;
|
||||||
|
strlist_iter;
|
||||||
|
strlist_iter = strlist_iter->next)
|
||||||
|
{
|
||||||
|
char *other_user_id = strlist_iter->d;
|
||||||
|
char *other_thing;
|
||||||
|
enum tofu_policy other_policy;
|
||||||
|
|
||||||
|
log_assert (strlist_iter->next);
|
||||||
|
strlist_iter = strlist_iter->next;
|
||||||
|
other_thing = strlist_iter->d;
|
||||||
|
|
||||||
|
if (opt.tofu_db_format == TOFU_DB_SPLIT)
|
||||||
|
other_policy = get_policy (dbs, fingerprint, other_thing, NULL);
|
||||||
|
else
|
||||||
|
other_policy = atoi (other_thing);
|
||||||
|
|
||||||
|
es_fprintf (fp, " %s (", other_user_id);
|
||||||
|
es_fprintf (fp, _("policy: %s"), tofu_policy_str (other_policy));
|
||||||
|
es_fprintf (fp, ")\n");
|
||||||
|
}
|
||||||
|
es_fprintf (fp, "\n");
|
||||||
|
|
||||||
|
free_strlist (other_user_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find other keys associated with this email address. */
|
||||||
|
/* 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, &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.) */
|
||||||
|
" when delta < -("STRINGIFY (TIME_AGO_FUTURE_IGNORE)") then -1\n"
|
||||||
|
" when delta < ("STRINGIFY (TIME_AGO_MEDIUM_THRESHOLD)")\n"
|
||||||
|
" then max(0,\n"
|
||||||
|
" round(delta / ("STRINGIFY (TIME_AGO_UNIT_SMALL)"))\n"
|
||||||
|
" * ("STRINGIFY (TIME_AGO_UNIT_SMALL)"))\n"
|
||||||
|
" when delta < ("STRINGIFY (TIME_AGO_LARGE_THRESHOLD)")\n"
|
||||||
|
" then round(delta / ("STRINGIFY (TIME_AGO_UNIT_MEDIUM)"))\n"
|
||||||
|
" * ("STRINGIFY (TIME_AGO_UNIT_MEDIUM)")\n"
|
||||||
|
" else round(delta / ("STRINGIFY (TIME_AGO_UNIT_LARGE)"))\n"
|
||||||
|
" * ("STRINGIFY (TIME_AGO_UNIT_LARGE)")\n"
|
||||||
|
" end time_ago,\n"
|
||||||
|
" delta time_ago_raw\n"
|
||||||
|
" from bindings\n"
|
||||||
|
" left join\n"
|
||||||
|
" (select *,\n"
|
||||||
|
" cast(strftime('%s','now') - sig_time as real) delta\n"
|
||||||
|
" from signatures) ss\n"
|
||||||
|
" on ss.binding = bindings.oid)\n"
|
||||||
|
" where email = ?\n"
|
||||||
|
" group by fingerprint, time_ago\n"
|
||||||
|
/* Make sure the current key is first. */
|
||||||
|
" order by fingerprint = ? asc, fingerprint desc, time_ago desc;\n",
|
||||||
|
SQLITE_ARG_STRING, email, SQLITE_ARG_STRING, fingerprint,
|
||||||
|
SQLITE_ARG_END);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
strlist_t strlist_iter;
|
||||||
|
|
||||||
|
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",
|
||||||
|
"The email address \"%s\" is"
|
||||||
|
" associated with %d keys:\n",
|
||||||
|
bindings_with_this_email_count),
|
||||||
|
email, bindings_with_this_email_count);
|
||||||
|
for (strlist_iter = bindings_with_this_email;
|
||||||
|
strlist_iter;
|
||||||
|
strlist_iter = strlist_iter->next)
|
||||||
|
es_fprintf (fp, " %s\n", strlist_iter->d);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *key = NULL;
|
||||||
|
|
||||||
|
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. */
|
||||||
|
signature_stats_prepend (&stats, fingerprint, TOFU_POLICY_AUTO, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
int this_key;
|
||||||
|
char *key_pp;
|
||||||
|
|
||||||
|
key = stats_iter->fingerprint;
|
||||||
|
this_key = strcmp (key, fingerprint) == 0;
|
||||||
|
key_pp = format_hexfingerprint (key, NULL, 0);
|
||||||
|
es_fprintf (fp, " %s (", key_pp);
|
||||||
|
if (this_key)
|
||||||
|
es_fprintf (fp, _("this key"));
|
||||||
|
else
|
||||||
|
es_fprintf (fp, _("policy: %s"),
|
||||||
|
tofu_policy_str (stats_iter->policy));
|
||||||
|
es_fputs ("):\n", fp);
|
||||||
|
xfree (key_pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
es_fputs (" ", fp);
|
||||||
|
if (stats_iter->time_ago == -1)
|
||||||
|
es_fprintf (fp, ngettext("%ld message signed in the future.",
|
||||||
|
"%ld messages signed in the future.",
|
||||||
|
stats_iter->count), stats_iter->count);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long t_scaled = time_ago_scale (stats_iter->time_ago);
|
||||||
|
|
||||||
|
/* TANSLATORS: This string is concatenated with one of
|
||||||
|
* the day/week/month strings to form one sentence. */
|
||||||
|
es_fprintf (fp, ngettext("%ld message signed",
|
||||||
|
"%ld messages signed",
|
||||||
|
stats_iter->count), stats_iter->count);
|
||||||
|
if (!stats_iter->count)
|
||||||
|
es_fputs (".", fp);
|
||||||
|
else if (stats_iter->time_ago < TIME_AGO_UNIT_MEDIUM)
|
||||||
|
es_fprintf (fp, ngettext(" over the past %ld day.",
|
||||||
|
" over the past %ld days.",
|
||||||
|
t_scaled), t_scaled);
|
||||||
|
else if (stats_iter->time_ago < TIME_AGO_UNIT_LARGE)
|
||||||
|
es_fprintf (fp, ngettext(" over the past %ld week.",
|
||||||
|
" over the past %ld weeks.",
|
||||||
|
t_scaled), t_scaled);
|
||||||
|
else
|
||||||
|
es_fprintf (fp, ngettext(" over the past %ld month.",
|
||||||
|
" over the past %ld months.",
|
||||||
|
t_scaled), t_scaled);
|
||||||
|
}
|
||||||
|
es_fputs ("\n", fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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. */
|
||||||
|
char *text = _("TOFU detected a binding conflict");
|
||||||
|
char *textbuf;
|
||||||
|
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 "
|
||||||
|
"address. However, people sometimes generate a new key if "
|
||||||
|
"their key is too old or they think it might be compromised. "
|
||||||
|
"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);
|
||||||
|
}
|
||||||
|
|
||||||
|
es_fputc ('\n', fp);
|
||||||
|
|
||||||
|
/* Add a NUL terminator. */
|
||||||
|
es_fputc (0, fp);
|
||||||
|
if (es_fclose_snatch (fp, (void **) &prompt, NULL))
|
||||||
|
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. */
|
||||||
|
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. */
|
||||||
|
choices = _("gG" "aA" "uU" "rR" "bB");
|
||||||
|
if (strlen (choices) != 10)
|
||||||
|
log_bug ("Bad TOFU conflict translation! Please report.");
|
||||||
|
|
||||||
|
response = cpr_get
|
||||||
|
("tofu.conflict",
|
||||||
|
_("(G)ood, (A)ccept once, (U)nknown, (R)eject once, (B)ad? "));
|
||||||
|
trim_spaces (response);
|
||||||
|
cpr_kill_prompt ();
|
||||||
|
if (*response == CONTROL_L)
|
||||||
|
tty_printf ("%s", prompt);
|
||||||
|
else if (strlen (response) == 1)
|
||||||
|
{
|
||||||
|
char *choice = strchr (choices, *response);
|
||||||
|
if (choice)
|
||||||
|
{
|
||||||
|
int c = ((size_t) choice - (size_t) choices) / 2;
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 0: /* Good. */
|
||||||
|
*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);
|
||||||
|
break;
|
||||||
|
case 2: /* Unknown. */
|
||||||
|
*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);
|
||||||
|
break;
|
||||||
|
case 4: /* Bad. */
|
||||||
|
*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))
|
||||||
|
{
|
||||||
|
/* If there's an error registering the
|
||||||
|
* binding, don't save the signature. */
|
||||||
|
*trust_level = _tofu_GET_TRUST_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xfree (response);
|
||||||
|
}
|
||||||
|
|
||||||
|
xfree (prompt);
|
||||||
|
|
||||||
|
signature_stats_free (stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Return the trust level (TRUST_NEVER, etc.) for the binding
|
/* Return the trust level (TRUST_NEVER, etc.) for the binding
|
||||||
* <FINGERPRINT, EMAIL> (email is already normalized). If no policy
|
* <FINGERPRINT, EMAIL> (email is already normalized). If no policy
|
||||||
* is registered, returns TOFU_POLICY_NONE. If an error occurs,
|
* is registered, returns TOFU_POLICY_NONE. If an error occurs,
|
||||||
@ -1621,12 +2026,11 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
|
|||||||
const char *fingerprint, const char *email,
|
const char *fingerprint, const char *email,
|
||||||
const char *user_id, int may_ask)
|
const char *user_id, int may_ask)
|
||||||
{
|
{
|
||||||
char *fingerprint_pp;
|
|
||||||
struct db *db;
|
struct db *db;
|
||||||
enum tofu_policy policy;
|
enum tofu_policy policy;
|
||||||
char *conflict = NULL;
|
char *conflict = NULL;
|
||||||
int rc;
|
int rc;
|
||||||
char *err = NULL;
|
char *sqerr = NULL;
|
||||||
strlist_t bindings_with_this_email = NULL;
|
strlist_t bindings_with_this_email = NULL;
|
||||||
int bindings_with_this_email_count;
|
int bindings_with_this_email_count;
|
||||||
int change_conflicting_to_ask = 0;
|
int change_conflicting_to_ask = 0;
|
||||||
@ -1649,8 +2053,6 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
|
|||||||
if (! db)
|
if (! db)
|
||||||
return _tofu_GET_TRUST_ERROR;
|
return _tofu_GET_TRUST_ERROR;
|
||||||
|
|
||||||
fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0);
|
|
||||||
|
|
||||||
policy = get_policy (dbs, fingerprint, email, &conflict);
|
policy = get_policy (dbs, fingerprint, email, &conflict);
|
||||||
if (policy == TOFU_POLICY_AUTO || policy == TOFU_POLICY_NONE)
|
if (policy == TOFU_POLICY_AUTO || policy == TOFU_POLICY_NONE)
|
||||||
{ /* See if the key is ultimately trusted. If so, we're done. */
|
{ /* See if the key is ultimately trusted. If so, we're done. */
|
||||||
@ -1692,7 +2094,7 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
|
|||||||
case TOFU_POLICY_UNKNOWN:
|
case TOFU_POLICY_UNKNOWN:
|
||||||
case TOFU_POLICY_BAD:
|
case TOFU_POLICY_BAD:
|
||||||
/* The saved judgement is auto -> auto, good, unknown or bad.
|
/* The saved judgement is auto -> auto, good, unknown or bad.
|
||||||
We don't need to ask the user anything. */
|
* We don't need to ask the user anything. */
|
||||||
if (DBG_TRUST)
|
if (DBG_TRUST)
|
||||||
log_debug ("TOFU: Known binding <%s, %s>'s policy: %s\n",
|
log_debug ("TOFU: Known binding <%s, %s>'s policy: %s\n",
|
||||||
fingerprint, email, tofu_policy_str (policy));
|
fingerprint, email, tofu_policy_str (policy));
|
||||||
@ -1711,7 +2113,7 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
|
|||||||
|
|
||||||
case TOFU_POLICY_NONE:
|
case TOFU_POLICY_NONE:
|
||||||
/* The binding is new, we need to check for conflicts. Case #3
|
/* The binding is new, we need to check for conflicts. Case #3
|
||||||
below. */
|
* below. */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _tofu_GET_POLICY_ERROR:
|
case _tofu_GET_POLICY_ERROR:
|
||||||
@ -1724,49 +2126,51 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
|
|||||||
|
|
||||||
|
|
||||||
/* We get here if:
|
/* We get here if:
|
||||||
|
*
|
||||||
1. The saved policy is auto and the default policy is ask
|
* 1. The saved policy is auto and the default policy is ask
|
||||||
(get_policy() == TOFU_POLICY_AUTO
|
* (get_policy() == TOFU_POLICY_AUTO
|
||||||
&& opt.tofu_default_policy == TOFU_POLICY_ASK)
|
* && opt.tofu_default_policy == TOFU_POLICY_ASK)
|
||||||
|
*
|
||||||
2. The saved policy is ask (either last time the user selected
|
* 2. The saved policy is ask (either last time the user selected
|
||||||
accept once or reject once or there was a conflict and this
|
* accept once or reject once or there was a conflict and this
|
||||||
binding's policy was changed from auto to ask)
|
* binding's policy was changed from auto to ask)
|
||||||
(policy == TOFU_POLICY_ASK), or,
|
* (policy == TOFU_POLICY_ASK), or,
|
||||||
|
*
|
||||||
3. We don't have a saved policy (policy == TOFU_POLICY_NONE)
|
* 3. We don't have a saved policy (policy == TOFU_POLICY_NONE)
|
||||||
(need to check for a conflict).
|
* (need to check for a conflict).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Look for conflicts. This is needed in all 3 cases.
|
/* Look for conflicts. This is needed in all 3 cases.
|
||||||
|
*
|
||||||
Get the fingerprints of any bindings that share the email
|
* Get the fingerprints of any bindings that share the email
|
||||||
address. Note: if the binding in question is in the DB, it will
|
* 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
|
* also be returned. Thus, if the result set is empty, then this is
|
||||||
a new binding. */
|
* a new binding. */
|
||||||
rc = gpgsql_stepx
|
rc = gpgsql_stepx
|
||||||
(db->db, &db->s.get_trust_bindings_with_this_email,
|
(db->db, &db->s.get_trust_bindings_with_this_email,
|
||||||
strings_collect_cb2, &bindings_with_this_email, &err,
|
strings_collect_cb2, &bindings_with_this_email, &sqerr,
|
||||||
"select distinct fingerprint from bindings where email = ?;",
|
"select distinct fingerprint from bindings where email = ?;",
|
||||||
SQLITE_ARG_STRING, email, SQLITE_ARG_END);
|
SQLITE_ARG_STRING, email, SQLITE_ARG_END);
|
||||||
if (rc)
|
if (rc)
|
||||||
{
|
{
|
||||||
log_error (_("error reading TOFU database: %s\n"), err);
|
log_error (_("error reading TOFU database: %s\n"), sqerr);
|
||||||
print_further_info ("listing fingerprints");
|
print_further_info ("listing fingerprints");
|
||||||
sqlite3_free (err);
|
sqlite3_free (sqerr);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bindings_with_this_email_count = strlist_length (bindings_with_this_email);
|
bindings_with_this_email_count = strlist_length (bindings_with_this_email);
|
||||||
if (bindings_with_this_email_count == 0
|
if (bindings_with_this_email_count == 0
|
||||||
&& opt.tofu_default_policy != TOFU_POLICY_ASK)
|
&& 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. */
|
|
||||||
{
|
{
|
||||||
|
/* 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
|
/* If we've seen this binding, then we've seen this email and
|
||||||
policy couldn't possibly be TOFU_POLICY_NONE. */
|
policy couldn't possibly be TOFU_POLICY_NONE. */
|
||||||
log_assert (policy == TOFU_POLICY_NONE);
|
log_assert (policy == TOFU_POLICY_NONE);
|
||||||
@ -1789,18 +2193,20 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (policy == TOFU_POLICY_NONE)
|
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
|
/* This is a new binding and we have a conflict. Mark any
|
||||||
requiring confirmation. Note: we delay this until after we ask
|
* conflicting bindings that have an automatic policy as now
|
||||||
for confirmation so that when the current policy is printed, it
|
* requiring confirmation. Note: we delay this until after we
|
||||||
is correct. */
|
* ask for confirmation so that when the current policy is
|
||||||
change_conflicting_to_ask = 1;
|
* printed, it is correct. */
|
||||||
|
change_conflicting_to_ask = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (! may_ask)
|
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). */
|
|
||||||
{
|
{
|
||||||
|
/* 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);
|
log_assert (policy == TOFU_POLICY_NONE);
|
||||||
|
|
||||||
if (record_binding (dbs, fingerprint, email, user_id,
|
if (record_binding (dbs, fingerprint, email, user_id,
|
||||||
@ -1812,412 +2218,52 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we get here, we need to ask the user about the binding. There
|
/* If we get here, we need to ask the user about the binding. */
|
||||||
are three ways we could end up here:
|
ask_about_binding (dbs, db,
|
||||||
|
&policy,
|
||||||
- This is a new binding and there is a conflict
|
&trust_level,
|
||||||
(policy == TOFU_POLICY_NONE && bindings_with_this_email_count > 0),
|
bindings_with_this_email_count,
|
||||||
|
bindings_with_this_email,
|
||||||
- This is a new binding and opt.tofu_default_policy is set to
|
conflict,
|
||||||
ask. (policy == TOFU_POLICY_NONE && opt.tofu_default_policy ==
|
fingerprint,
|
||||||
TOFU_POLICY_ASK), or,
|
email,
|
||||||
|
user_id);
|
||||||
- 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;
|
|
||||||
|
|
||||||
fp = es_fopenmem (0, "rw,samethread");
|
|
||||||
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);
|
|
||||||
|
|
||||||
es_fputs (text, 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;
|
|
||||||
|
|
||||||
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. */
|
|
||||||
db_key = getdb (dbs, fingerprint, DB_KEY);
|
|
||||||
else
|
|
||||||
db_key = db;
|
|
||||||
|
|
||||||
if (db_key)
|
|
||||||
{
|
|
||||||
rc = gpgsql_stepx
|
|
||||||
(db_key->db, &db_key->s.get_trust_gather_other_user_ids,
|
|
||||||
strings_collect_cb2, &other_user_ids, &err,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other_user_ids)
|
|
||||||
{
|
|
||||||
strlist_t strlist_iter;
|
|
||||||
|
|
||||||
es_fprintf (fp, _("Known user IDs associated with this key:\n"));
|
|
||||||
for (strlist_iter = other_user_ids;
|
|
||||||
strlist_iter;
|
|
||||||
strlist_iter = strlist_iter->next)
|
|
||||||
{
|
|
||||||
char *other_user_id = strlist_iter->d;
|
|
||||||
char *other_thing;
|
|
||||||
enum tofu_policy other_policy;
|
|
||||||
|
|
||||||
log_assert (strlist_iter->next);
|
|
||||||
strlist_iter = strlist_iter->next;
|
|
||||||
other_thing = strlist_iter->d;
|
|
||||||
|
|
||||||
if (opt.tofu_db_format == TOFU_DB_SPLIT)
|
|
||||||
other_policy = get_policy (dbs, fingerprint, other_thing, NULL);
|
|
||||||
else
|
|
||||||
other_policy = atoi (other_thing);
|
|
||||||
|
|
||||||
es_fprintf (fp, " %s (", other_user_id);
|
|
||||||
es_fprintf (fp, _("policy: %s"), tofu_policy_str (other_policy));
|
|
||||||
es_fprintf (fp, ")\n");
|
|
||||||
}
|
|
||||||
es_fprintf (fp, "\n");
|
|
||||||
|
|
||||||
free_strlist (other_user_ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find other keys associated with this email address. */
|
|
||||||
/* XXX: 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,
|
|
||||||
"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.) */
|
|
||||||
" when delta < -("STRINGIFY (TIME_AGO_FUTURE_IGNORE)") then -1\n"
|
|
||||||
" when delta < ("STRINGIFY (TIME_AGO_MEDIUM_THRESHOLD)")\n"
|
|
||||||
" then max(0,\n"
|
|
||||||
" round(delta / ("STRINGIFY (TIME_AGO_UNIT_SMALL)"))\n"
|
|
||||||
" * ("STRINGIFY (TIME_AGO_UNIT_SMALL)"))\n"
|
|
||||||
" when delta < ("STRINGIFY (TIME_AGO_LARGE_THRESHOLD)")\n"
|
|
||||||
" then round(delta / ("STRINGIFY (TIME_AGO_UNIT_MEDIUM)"))\n"
|
|
||||||
" * ("STRINGIFY (TIME_AGO_UNIT_MEDIUM)")\n"
|
|
||||||
" else round(delta / ("STRINGIFY (TIME_AGO_UNIT_LARGE)"))\n"
|
|
||||||
" * ("STRINGIFY (TIME_AGO_UNIT_LARGE)")\n"
|
|
||||||
" end time_ago,\n"
|
|
||||||
" delta time_ago_raw\n"
|
|
||||||
" from bindings\n"
|
|
||||||
" left join\n"
|
|
||||||
" (select *,\n"
|
|
||||||
" cast(strftime('%s','now') - sig_time as real) delta\n"
|
|
||||||
" from signatures) ss\n"
|
|
||||||
" on ss.binding = bindings.oid)\n"
|
|
||||||
" where email = ?\n"
|
|
||||||
" group by fingerprint, time_ago\n"
|
|
||||||
/* Make sure the current key is first. */
|
|
||||||
" order by fingerprint = ? asc, fingerprint desc, time_ago desc;\n",
|
|
||||||
SQLITE_ARG_STRING, email, SQLITE_ARG_STRING, fingerprint,
|
|
||||||
SQLITE_ARG_END);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
strlist_t strlist_iter;
|
|
||||||
|
|
||||||
log_error (_("error gathering signature stats: %s\n"), err);
|
|
||||||
sqlite3_free (err);
|
|
||||||
err = NULL;
|
|
||||||
|
|
||||||
es_fprintf (fp, ngettext("The email address \"%s\" is"
|
|
||||||
" associated with %d key:\n",
|
|
||||||
"The email address \"%s\" is"
|
|
||||||
" associated with %d keys:\n",
|
|
||||||
bindings_with_this_email_count),
|
|
||||||
email, bindings_with_this_email_count);
|
|
||||||
for (strlist_iter = bindings_with_this_email;
|
|
||||||
strlist_iter;
|
|
||||||
strlist_iter = strlist_iter->next)
|
|
||||||
es_fprintf (fp, " %s\n", strlist_iter->d);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char *key = NULL;
|
|
||||||
|
|
||||||
if (! stats || strcmp (stats->fingerprint, fingerprint) != 0)
|
|
||||||
/* 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. */
|
|
||||||
signature_stats_prepend (&stats, fingerprint, TOFU_POLICY_AUTO, 0, 0);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
int this_key;
|
|
||||||
char *key_pp;
|
|
||||||
key = stats_iter->fingerprint;
|
|
||||||
this_key = strcmp (key, fingerprint) == 0;
|
|
||||||
key_pp = format_hexfingerprint (key, NULL, 0);
|
|
||||||
es_fprintf (fp, " %s (", key_pp);
|
|
||||||
if (this_key)
|
|
||||||
es_fprintf (fp, _("this key"));
|
|
||||||
else
|
|
||||||
es_fprintf (fp, _("policy: %s"),
|
|
||||||
tofu_policy_str (stats_iter->policy));
|
|
||||||
es_fputs ("):\n", fp);
|
|
||||||
xfree (key_pp);
|
|
||||||
}
|
|
||||||
|
|
||||||
es_fputs (" ", fp);
|
|
||||||
if (stats_iter->time_ago == -1)
|
|
||||||
es_fprintf (fp, ngettext("%ld message signed in the future.",
|
|
||||||
"%ld messages signed in the future.",
|
|
||||||
stats_iter->count), stats_iter->count);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
long t_scaled = time_ago_scale (stats_iter->time_ago);
|
|
||||||
|
|
||||||
/* TANSLATORS: This string is concatenated with one of
|
|
||||||
* the day/week/month strings to form one sentence. */
|
|
||||||
es_fprintf (fp, ngettext("%ld message signed",
|
|
||||||
"%ld messages signed",
|
|
||||||
stats_iter->count), stats_iter->count);
|
|
||||||
if (!stats_iter->count)
|
|
||||||
es_fputs (".", fp);
|
|
||||||
else if (stats_iter->time_ago < TIME_AGO_UNIT_MEDIUM)
|
|
||||||
es_fprintf (fp, ngettext(" over the past %ld day.",
|
|
||||||
" over the past %ld days.",
|
|
||||||
t_scaled), t_scaled);
|
|
||||||
else if (stats_iter->time_ago < TIME_AGO_UNIT_LARGE)
|
|
||||||
es_fprintf (fp, ngettext(" over the past %ld week.",
|
|
||||||
" over the past %ld weeks.",
|
|
||||||
t_scaled), t_scaled);
|
|
||||||
else
|
|
||||||
es_fprintf (fp, ngettext(" over the past %ld month.",
|
|
||||||
" over the past %ld months.",
|
|
||||||
t_scaled), t_scaled);
|
|
||||||
}
|
|
||||||
es_fputs ("\n", fp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_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. */
|
|
||||||
char *text = _("TOFU detected a binding conflict");
|
|
||||||
char *textbuf;
|
|
||||||
if (strcmp (text, "TOFU detected a binding conflict") == 0)
|
|
||||||
/* No translation. Use the English text. */
|
|
||||||
text =
|
|
||||||
"Normally, there is only a single key associated with an email "
|
|
||||||
"address. However, people sometimes generate a new key if "
|
|
||||||
"their key is too old or they think it might be compromised. "
|
|
||||||
"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);
|
|
||||||
}
|
|
||||||
|
|
||||||
es_fputc ('\n', fp);
|
|
||||||
|
|
||||||
/* Add a NUL terminator. */
|
|
||||||
es_fputc (0, fp);
|
|
||||||
if (es_fclose_snatch (fp, (void **) &prompt, NULL))
|
|
||||||
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. */
|
|
||||||
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. */
|
|
||||||
choices = _("gG" "aA" "uU" "rR" "bB");
|
|
||||||
if (strlen (choices) != 10)
|
|
||||||
log_bug ("Bad TOFU conflict translation! Please report.");
|
|
||||||
|
|
||||||
response = cpr_get
|
|
||||||
("tofu.conflict",
|
|
||||||
_("(G)ood, (A)ccept once, (U)nknown, (R)eject once, (B)ad? "));
|
|
||||||
trim_spaces (response);
|
|
||||||
cpr_kill_prompt ();
|
|
||||||
if (*response == CONTROL_L)
|
|
||||||
tty_printf ("%s", prompt);
|
|
||||||
else if (strlen (response) == 1)
|
|
||||||
{
|
|
||||||
char *choice = strchr (choices, *response);
|
|
||||||
if (choice)
|
|
||||||
{
|
|
||||||
int c = ((size_t) choice - (size_t) choices) / 2;
|
|
||||||
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case 0: /* Good. */
|
|
||||||
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);
|
|
||||||
break;
|
|
||||||
case 2: /* Unknown. */
|
|
||||||
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);
|
|
||||||
break;
|
|
||||||
case 4: /* Bad. */
|
|
||||||
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)
|
|
||||||
/* If there's an error registering the
|
|
||||||
binding, don't save the signature. */
|
|
||||||
trust_level = _tofu_GET_TRUST_ERROR;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xfree (response);
|
|
||||||
}
|
|
||||||
|
|
||||||
xfree (prompt);
|
|
||||||
|
|
||||||
signature_stats_free (stats);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
{
|
||||||
conflicting with itself. */
|
/* If we weren't allowed to ask, also update this key as
|
||||||
rc = gpgsql_exec_printf
|
conflicting with itself. */
|
||||||
(db->db, NULL, NULL, &err,
|
rc = gpgsql_exec_printf
|
||||||
"update bindings set policy = %d, conflict = %Q"
|
(db->db, NULL, NULL, &sqerr,
|
||||||
" where email = %Q"
|
"update bindings set policy = %d, conflict = %Q"
|
||||||
" and (policy = %d or (policy = %d and fingerprint = %Q));",
|
" where email = %Q"
|
||||||
TOFU_POLICY_ASK, fingerprint, email, TOFU_POLICY_AUTO,
|
" and (policy = %d or (policy = %d and fingerprint = %Q));",
|
||||||
TOFU_POLICY_ASK, fingerprint);
|
TOFU_POLICY_ASK, fingerprint, email, TOFU_POLICY_AUTO,
|
||||||
|
TOFU_POLICY_ASK, fingerprint);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
rc = gpgsql_exec_printf
|
{
|
||||||
(db->db, NULL, NULL, &err,
|
rc = gpgsql_exec_printf
|
||||||
"update bindings set policy = %d, conflict = %Q"
|
(db->db, NULL, NULL, &sqerr,
|
||||||
" where email = %Q and fingerprint != %Q and policy = %d;",
|
"update bindings set policy = %d, conflict = %Q"
|
||||||
TOFU_POLICY_ASK, fingerprint, email, fingerprint, TOFU_POLICY_AUTO);
|
" where email = %Q and fingerprint != %Q and policy = %d;",
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user