[Pkg-gnupg-commit] [gnupg2] 80/102: gpg: Split tofu's get_trust function into several smaller ones.
Daniel Kahn Gillmor
dkg at fifthhorseman.net
Fri Jun 17 00:14:58 UTC 2016
This is an automated email from the git hooks/post-receive script.
dkg pushed a commit to branch experimental
in repository gnupg2.
commit 1affdf1efc42ed22dc023c92ca5134d5bcbf2686
Author: Werner Koch <wk at gnupg.org>
Date: Tue Jun 14 12:02:22 2016 +0200
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 at gnupg.org>
---
g10/tofu.c | 914 ++++++++++++++++++++++++++++++++-----------------------------
1 file changed, 480 insertions(+), 434 deletions(-)
diff --git a/g10/tofu.c b/g10/tofu.c
index d11a8de..4b752f7 100644
--- a/g10/tofu.c
+++ b/g10/tofu.c
@@ -1602,6 +1602,411 @@ get_policy (tofu_dbs_t dbs, const char *fingerprint, const char *email,
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
* <FINGERPRINT, EMAIL> (email is already normalized). If no policy
* 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 *user_id, int may_ask)
{
- char *fingerprint_pp;
struct db *db;
enum tofu_policy policy;
char *conflict = NULL;
int rc;
- char *err = NULL;
+ char *sqerr = NULL;
strlist_t bindings_with_this_email = NULL;
int bindings_with_this_email_count;
int change_conflicting_to_ask = 0;
@@ -1649,8 +2053,6 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
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. */
@@ -1692,7 +2094,7 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
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. */
+ * 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));
@@ -1711,7 +2113,7 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
case TOFU_POLICY_NONE:
/* The binding is new, we need to check for conflicts. Case #3
- below. */
+ * below. */
break;
case _tofu_GET_POLICY_ERROR:
@@ -1724,49 +2126,51 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
/* 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).
+ *
+ * 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. */
+ *
+ * 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,
+ 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"), err);
+ log_error (_("error reading TOFU database: %s\n"), sqerr);
print_further_info ("listing fingerprints");
- sqlite3_free (err);
+ 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. */
{
+ /* 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);
@@ -1789,18 +2193,20 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
}
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;
+ {
+ /* 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). */
{
+ /* 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,
@@ -1812,412 +2218,52 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
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;
-
- 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);
- }
+ /* 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,
- "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);
+ {
+ /* If we weren't allowed to ask, also update this key as
+ conflicting with itself. */
+ rc = gpgsql_exec_printf
+ (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,
- "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);
+ {
+ rc = gpgsql_exec_printf
+ (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);
+ }
+
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;
}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-gnupg/gnupg2.git
More information about the Pkg-gnupg-commit
mailing list