g10: Record and show statistics for encrypted messages when using TOFU

* g10/tofu.c: Include "sqrtu32.h".
(struct tofu_dbs_s.s): Rename get_trust_gather_other_keys to
get_trust_gather_signature_stats.  Add new field
get_trust_gather_encryption_stats.
(initdb): Create the encryptions table.
(ask_about_binding): Show the encryption statistics too.
(tofu_register): Rename from this...
(tofu_register_signature): ... to this and update callers.
(tofu_register_encryption): New function.
(write_stats_status): Add parameters encryption_count,
encryption_first_done and encryption_most_recent.  Update callers.
Compute the trust using the euclidean distance of the signature and
signature count.  Compare with twice the threshold.  Include
encryption count information in the TFS and TOFU_STATS lines.
(show_statistics): Also get information about the encrypted messages.
* g10/trustdb.c (tdb_get_validity_core): Use it.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
This commit is contained in:
Neal H. Walfield 2016-09-06 15:45:38 +02:00
parent a9e6db6c7e
commit 875ac9216f
6 changed files with 474 additions and 229 deletions

View File

@ -238,8 +238,10 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB:
- Field 4 :: signcount - The number of signatures seen.
- Field 5 :: encrcount - The number of encryptions done.
- Field 6 :: policy - A string with the policy
- Field 7 :: first-seen - a timestamp or 0 if not known.
- Field 8 :: most-recent-seen - a timestamp or 0 if not known.
- Field 7 :: signture-first-seen - a timestamp or 0 if not known.
- Field 8 :: signature-most-recent-seen - a timestamp or 0 if not known.
- Field 9 :: encryption-first-done - a timestamp or 0 if not known.
- Field 10 :: encryption-most-recent-done - a timestamp or 0 if not known.
*** TRU - Trust database information
Example for a "tru" trust base record:
@ -715,7 +717,7 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB:
userid encoded in UTF-8 and percent escaped. The fingerprint is
indentical for all TOFU_USER lines up to a NEWSIG line.
*** TOFU_STATS <validity> <sign-count> 0 [<policy> [<tm1> <tm2>]]
*** TOFU_STATS <validity> <sign-count> 0 [<policy> [<tm1> <tm2> <tm3> <tm4>]]
Statistics for the current user id.
@ -734,9 +736,11 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB:
- ask :: Policy is "ask"
- unknown :: Policy is not known.
TM1 ist the time the first messages was verified. TM2 is the time
the most recent message was verified. Both may either be seconds
since Epoch or an ISO time string (yyyymmddThhmmss).
TM1 ist the time the first message was verified. TM2 is the time
the most recent message was verified. TM3 is the time the first
message was encrypted. TM4 is the most recent encryption. All may
either be seconds since Epoch or an ISO time string
(yyyymmddThhmmss).
*** TOFU_STATS_SHORT <long_string>

View File

@ -74,7 +74,7 @@ trust_source = trustdb.c trustdb.h tdbdump.c tdbio.c tdbio.h
endif
if USE_TOFU
tofu_source = tofu.h tofu.c gpgsql.c gpgsql.h
tofu_source = tofu.h tofu.c gpgsql.c gpgsql.h sqrtu32.c sqrtu32.h
else
tofu_source =
endif

View File

@ -1314,6 +1314,29 @@ build_pk_list (ctrl_t ctrl, strlist_t rcpts, PK_LIST *ret_pk_list)
rc = GPG_ERR_NO_USER_ID;
}
#ifdef USE_TOFU
if (! rc && (opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU))
{
PK_LIST iter;
for (iter = pk_list; iter; iter = iter->next)
{
int rc2;
/* Note: we already resolved any conflict when looking up
the key. Don't annoy the user again if she selected
accept once. */
rc2 = tofu_register_encryption (ctrl, iter->pk, NULL, 0);
if (rc2)
log_info ("WARNING: Failed to register encryption to %s"
" with TOFU engine\n",
keystr (pk_main_keyid (iter->pk)));
else if (DBG_TRUST)
log_debug ("Registered encryption to %s with TOFU DB.\n",
keystr (pk_main_keyid (iter->pk)));
}
}
#endif /*USE_TOFU*/
fail:
if ( rc )

View File

@ -41,6 +41,7 @@
#include "mkdir_p.h"
#include "gpgsql.h"
#include "status.h"
#include "sqrtu32.h"
#include "tofu.h"
@ -76,7 +77,8 @@ struct tofu_dbs_s
sqlite3_stmt *get_policy_select_policy_and_conflict;
sqlite3_stmt *get_trust_bindings_with_this_email;
sqlite3_stmt *get_trust_gather_other_user_ids;
sqlite3_stmt *get_trust_gather_other_keys;
sqlite3_stmt *get_trust_gather_signature_stats;
sqlite3_stmt *get_trust_gather_encryption_stats;
sqlite3_stmt *register_already_seen;
sqlite3_stmt *register_insert;
} s;
@ -649,6 +651,19 @@ initdb (sqlite3 *db)
}
out:
if (! rc)
{
/* Early version of the v1 format did not include the encryption
table. Add it. */
sqlite3_exec (db,
"create table if not exists encryptions"
" (binding INTEGER NOT NULL,"
" time INTEGER);"
"create index if not exists encryptions_binding"
" on encryptions (binding);\n",
NULL, NULL, &err);
}
if (rc)
{
rc = sqlite3_exec (db, "rollback;", NULL, NULL, &err);
@ -1384,39 +1399,42 @@ ask_about_binding (ctrl_t ctrl,
strlist_rev (&conflict_set);
for (iter = conflict_set; iter && ! rc; iter = iter->next)
{
#define STATS_SQL(table, time, sign) \
"select fingerprint, policy, time_ago, count(*)\n" \
" from\n" \
" (select bindings.*,\n" \
" "sign" case\n" \
" when delta ISNULL then 1\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 2\n" \
" when delta < ("STRINGIFY (TIME_AGO_SMALL_THRESHOLD)")\n" \
" then 3\n" \
" when delta < ("STRINGIFY (TIME_AGO_MEDIUM_THRESHOLD)")\n" \
" then 4\n" \
" when delta < ("STRINGIFY (TIME_AGO_LARGE_THRESHOLD)")\n" \
" then 5\n" \
" else 6\n" \
" end time_ago,\n" \
" delta time_ago_raw\n" \
" from bindings\n" \
" left join\n" \
" (select *,\n" \
" cast(strftime('%s','now') - " time " as real) delta\n" \
" from " table ") ss\n" \
" on ss.binding = bindings.oid)\n" \
" where email = ? and fingerprint = ?\n" \
" group by time_ago\n" \
/* Make sure the current key is first. */ \
" order by time_ago desc;\n"
rc = gpgsql_stepx
(dbs->db, &dbs->s.get_trust_gather_other_keys,
(dbs->db, &dbs->s.get_trust_gather_signature_stats,
signature_stats_collect_cb, &stats, &sqerr,
"select fingerprint, policy, time_ago, count(*)\n"
" from\n"
" (select bindings.*,\n"
" case\n"
" when delta ISNULL then 1\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 2\n"
" when delta < ("STRINGIFY (TIME_AGO_SMALL_THRESHOLD)")\n"
" then 3\n"
" when delta < ("STRINGIFY (TIME_AGO_MEDIUM_THRESHOLD)")\n"
" then 4\n"
" when delta < ("STRINGIFY (TIME_AGO_LARGE_THRESHOLD)")\n"
" then 5\n"
" else 6\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 = ? and fingerprint = ?\n"
" group by time_ago\n"
/* Make sure the current key is first. */
" order by time_ago desc;\n",
STATS_SQL ("signatures", "sig_time", ""),
GPGSQL_ARG_STRING, email,
GPGSQL_ARG_STRING, iter->d,
GPGSQL_ARG_END);
@ -1426,6 +1444,23 @@ ask_about_binding (ctrl_t ctrl,
if (!stats || strcmp (iter->d, stats->fingerprint) != 0)
/* No stats for this binding. Add a dummy entry. */
signature_stats_prepend (&stats, iter->d, TOFU_POLICY_AUTO, 1, 1);
rc = gpgsql_stepx
(dbs->db, &dbs->s.get_trust_gather_encryption_stats,
signature_stats_collect_cb, &stats, &sqerr,
STATS_SQL ("encryptions", "time", "-"),
GPGSQL_ARG_STRING, email,
GPGSQL_ARG_STRING, iter->d,
GPGSQL_ARG_END);
if (rc)
break;
#undef STATS_SQL
if (!stats || strcmp (iter->d, stats->fingerprint) != 0
|| stats->time_ago > 0)
/* No stats for this binding. Add a dummy entry. */
signature_stats_prepend (&stats, iter->d, TOFU_POLICY_AUTO, -1, 1);
}
end_transaction (ctrl, 0);
strlist_rev (&conflict_set);
@ -1459,6 +1494,13 @@ ask_about_binding (ctrl_t ctrl,
email);
for (stats_iter = stats; stats_iter; stats_iter = stats_iter->next)
{
#if 0
log_debug ("%s: time_ago: %ld; count: %ld\n",
stats_iter->fingerprint,
stats_iter->time_ago,
stats_iter->count);
#endif
if (! key || strcmp (key, stats_iter->fingerprint))
{
int this_key;
@ -1499,7 +1541,7 @@ ask_about_binding (ctrl_t ctrl,
seen_in_past = 0;
}
if (stats_iter->time_ago == 1)
if (abs(stats_iter->time_ago) == 1)
{
/* The 1 in this case is the NULL entry. */
log_assert (stats_iter->count == 1);
@ -1510,12 +1552,18 @@ ask_about_binding (ctrl_t ctrl,
es_fputs (" ", fp);
/* TANSLATORS: This string is concatenated with one of
* the day/week/month strings to form one sentence. */
es_fprintf (fp, ngettext("Verified %d message",
"Verified %d messages",
seen_in_past), seen_in_past);
if (stats_iter->time_ago > 0)
es_fprintf (fp, ngettext("Verified %d message",
"Verified %d messages",
seen_in_past), seen_in_past);
else
es_fprintf (fp, ngettext("Encrypted %d message",
"Encrypted %d messages",
seen_in_past), seen_in_past);
if (!stats_iter->count)
es_fputs (".", fp);
else if (stats_iter->time_ago == 2)
else if (abs(stats_iter->time_ago) == 2)
{
es_fprintf (fp, "in the future.");
/* Reset it. */
@ -1523,25 +1571,25 @@ ask_about_binding (ctrl_t ctrl,
}
else
{
if (stats_iter->time_ago == 3)
if (abs(stats_iter->time_ago) == 3)
es_fprintf (fp, ngettext(" over the past days.",
" over the past %d days.",
seen_in_past),
TIME_AGO_SMALL_THRESHOLD
/ TIME_AGO_UNIT_SMALL);
else if (stats_iter->time_ago == 4)
else if (abs(stats_iter->time_ago) == 4)
es_fprintf (fp, ngettext(" over the past month.",
" over the past %d months.",
seen_in_past),
TIME_AGO_MEDIUM_THRESHOLD
/ TIME_AGO_UNIT_MEDIUM);
else if (stats_iter->time_ago == 5)
else if (abs(stats_iter->time_ago) == 5)
es_fprintf (fp, ngettext(" over the past year.",
" over the past %d years.",
seen_in_past),
TIME_AGO_LARGE_THRESHOLD
/ TIME_AGO_UNIT_LARGE);
else if (stats_iter->time_ago == 6)
else if (abs(stats_iter->time_ago) == 6)
es_fprintf (fp, _(" in the past."));
else
log_assert (! "Broken SQL.\n");
@ -2349,46 +2397,59 @@ time_ago_str (long long int t)
/* If FP is NULL, write TOFU_STATS status line. If FP is not NULL
* write a "tfs" record to that stream. */
static void
write_stats_status (estream_t fp, long messages, enum tofu_policy policy,
unsigned long first_seen,
unsigned long most_recent_seen)
write_stats_status (estream_t fp,
enum tofu_policy policy,
unsigned long signature_count,
unsigned long signature_first_seen,
unsigned long signature_most_recent,
unsigned long encryption_count,
unsigned long encryption_first_done,
unsigned long encryption_most_recent)
{
const char *validity;
/* Use the euclidean distance rather then the sum of the magnitudes
to ensure a balance between verified signatures and encrypted
messages. */
float messages = sqrtu32 (signature_count) + sqrtu32 (encryption_count);
if (messages < 1)
validity = "1"; /* Key without history. */
else if (messages < BASIC_TRUST_THRESHOLD)
else if (messages < sqrtu32 (2 * BASIC_TRUST_THRESHOLD))
validity = "2"; /* Key with too little history. */
else if (messages < FULL_TRUST_THRESHOLD)
else if (messages < sqrtu32 (2 * FULL_TRUST_THRESHOLD))
validity = "3"; /* Key with enough history for basic trust. */
else
validity = "4"; /* Key with a lot of history. */
if (fp)
{
es_fprintf (fp, "tfs:1:%s:%ld:0:%s:%lu:%lu:\n",
validity, messages,
es_fprintf (fp, "tfs:1:%s:%ld:%ld:%s:%lu:%lu:%lu:%lu:\n",
validity, signature_count, encryption_count,
tofu_policy_str (policy),
first_seen, most_recent_seen);
signature_first_seen, signature_most_recent,
encryption_first_done, encryption_most_recent);
}
else
{
char numbuf1[35];
char numbuf2[35];
char numbuf3[35];
char numbuf4[35];
char numbuf5[35];
char numbuf6[35];
snprintf (numbuf1, sizeof numbuf1, " %ld", messages);
*numbuf2 = *numbuf3 = 0;
if (first_seen && most_recent_seen)
{
snprintf (numbuf2, sizeof numbuf2, " %lu", first_seen);
snprintf (numbuf3, sizeof numbuf3, " %lu", most_recent_seen);
}
snprintf (numbuf1, sizeof numbuf1, " %ld", signature_count);
snprintf (numbuf2, sizeof numbuf2, " %ld", encryption_count);
snprintf (numbuf3, sizeof numbuf3, " %lu", signature_first_seen);
snprintf (numbuf4, sizeof numbuf4, " %lu", signature_most_recent);
snprintf (numbuf5, sizeof numbuf5, " %lu", encryption_first_done);
snprintf (numbuf6, sizeof numbuf6, " %lu", encryption_most_recent);
write_status_strings (STATUS_TOFU_STATS,
validity, numbuf1, " 0",
validity, numbuf1, numbuf2,
" ", tofu_policy_str (policy),
numbuf2, numbuf3,
numbuf3, numbuf4, numbuf5, numbuf6,
NULL);
}
}
@ -2401,13 +2462,24 @@ show_statistics (tofu_dbs_t dbs, const char *fingerprint,
const char *email, const char *user_id,
estream_t outfp)
{
unsigned long now = gnupg_get_time ();
enum tofu_policy policy = get_policy (dbs, fingerprint, email, NULL);
char *fingerprint_pp;
int rc;
strlist_t strlist = NULL;
char *err = NULL;
unsigned long signature_first_seen = 0;
unsigned long signature_most_recent = 0;
unsigned long signature_count = 0;
unsigned long encryption_first_done = 0;
unsigned long encryption_most_recent = 0;
unsigned long encryption_count = 0;
fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0);
/* Get the signature stats. */
rc = gpgsql_exec_printf
(dbs->db, strings_collect_cb, &strlist, &err,
"select count (*), min (signatures.time), max (signatures.time)\n"
@ -2423,191 +2495,217 @@ show_statistics (tofu_dbs_t dbs, const char *fingerprint,
goto out;
}
if (strlist)
{
log_assert (strlist->next);
log_assert (strlist->next->next);
log_assert (! strlist->next->next->next);
string_to_long (&signature_count, strlist->d, -1, __LINE__);
string_to_long (&signature_first_seen, strlist->next->d, -1, __LINE__);
string_to_long (&signature_most_recent,
strlist->next->next->d, -1, __LINE__);
free_strlist (strlist);
strlist = NULL;
}
/* Get the encryption stats. */
rc = gpgsql_exec_printf
(dbs->db, strings_collect_cb, &strlist, &err,
"select count (*), min (encryptions.time), max (encryptions.time)\n"
" from encryptions\n"
" left join bindings on encryptions.binding = bindings.oid\n"
" where fingerprint = %Q and email = %Q;",
fingerprint, email);
if (rc)
{
log_error (_("error reading TOFU database: %s\n"), err);
print_further_info ("getting statistics");
sqlite3_free (err);
goto out;
}
if (strlist)
{
log_assert (strlist->next);
log_assert (strlist->next->next);
log_assert (! strlist->next->next->next);
string_to_long (&encryption_count, strlist->d, -1, __LINE__);
string_to_long (&encryption_first_done, strlist->next->d, -1, __LINE__);
string_to_long (&encryption_most_recent,
strlist->next->next->d, -1, __LINE__);
free_strlist (strlist);
strlist = NULL;
}
if (!outfp)
write_status_text_and_buffer (STATUS_TOFU_USER, fingerprint,
email, strlen (email), 0);
if (! strlist)
write_stats_status (outfp, policy,
signature_count,
signature_first_seen,
signature_most_recent,
encryption_count,
encryption_first_done,
encryption_most_recent);
if (!outfp)
{
if (!outfp)
log_info (_("Have never verified a message signed by key %s!\n"),
fingerprint_pp);
write_stats_status (outfp, 0, TOFU_POLICY_NONE, 0, 0);
}
else
{
unsigned long now = gnupg_get_time ();
signed long messages;
unsigned long first_seen;
unsigned long most_recent_seen;
estream_t fp;
char *msg;
log_assert (strlist_length (strlist) == 3);
fp = es_fopenmem (0, "rw,samethread");
if (! fp)
log_fatal ("error creating memory stream: %s\n",
gpg_strerror (gpg_error_from_syserror()));
string_to_long (&messages, strlist->d, -1, __LINE__);
if (messages == 0 && *strlist->next->d == '\0')
{ /* min(NULL) => NULL => "". */
first_seen = 0;
most_recent_seen = 0;
if (signature_count == 0)
{
es_fprintf (fp, _("Verified %ld messages signed by \"%s\"."),
0L, user_id);
es_fputc ('\n', fp);
}
else
{
string_to_ulong (&first_seen, strlist->next->d, -1, __LINE__);
if (first_seen > now)
{
log_debug ("time-warp - tofu DB has a future value (%lu, %lu)\n",
first_seen, now);
first_seen = now;
}
string_to_ulong (&most_recent_seen, strlist->next->next->d, -1,
__LINE__);
if (most_recent_seen > now)
{
log_debug ("time-warp - tofu DB has a future value (%lu, %lu)\n",
most_recent_seen, now);
most_recent_seen = now;
}
}
if (messages == -1 || first_seen == -1)
{
write_stats_status (outfp, 0, TOFU_POLICY_NONE, 0, 0);
if (!outfp)
log_info (_("Failed to collect signature statistics for \"%s\"\n"
"(key %s)\n"),
user_id, fingerprint_pp);
char *first_seen_ago_str = time_ago_str (now - signature_first_seen);
/* TRANSLATORS: The final %s is replaced by a string like
"7 months, 1 day, 5 minutes, 0 seconds". */
es_fprintf (fp,
ngettext("Verified %ld message signed by \"%s\"\n"
"in the past %s.",
"Verified %ld messages signed by \"%s\"\n"
"in the past %s.",
signature_count),
signature_count, user_id, first_seen_ago_str);
if (signature_count > 1)
{
char *tmpstr = time_ago_str (now - signature_most_recent);
es_fputs (" ", fp);
es_fprintf (fp, _("The most recent message was"
" verified %s ago."), tmpstr);
xfree (tmpstr);
}
xfree (first_seen_ago_str);
}
else if (outfp)
es_fprintf (fp, " ");
if (encryption_count == 0)
{
write_stats_status (outfp, messages,
get_policy (dbs, fingerprint, email, NULL),
first_seen, most_recent_seen);
es_fprintf (fp, _("Encrypted %ld messages to \"%s\"."),
0L, user_id);
es_fputc ('\n', fp);
}
else
{
enum tofu_policy policy = get_policy (dbs, fingerprint, email, NULL);
estream_t fp;
char *msg;
{
char *first_done_ago_str = time_ago_str (now - encryption_first_done);
write_stats_status (NULL, messages,
policy,
first_seen, most_recent_seen);
/* TRANSLATORS: The final %s is replaced by a string like
"7 months, 1 day, 5 minutes, 0 seconds". */
es_fprintf (fp,
ngettext("Encrypted %ld message to \"%s\"\n"
"in the past %s.",
"Encrypted %ld messages to \"%s\"\n"
"in the past %s.",
encryption_count),
encryption_count, user_id, first_done_ago_str);
fp = es_fopenmem (0, "rw,samethread");
if (! fp)
log_fatal ("error creating memory stream: %s\n",
gpg_strerror (gpg_error_from_syserror()));
if (messages == 0)
if (encryption_count > 1)
{
es_fprintf (fp, _("Verified %ld messages signed by \"%s\"."),
0L, user_id);
es_fputc ('\n', fp);
char *tmpstr = time_ago_str (now - encryption_most_recent);
es_fputs (" ", fp);
es_fprintf (fp, _("The most recent message was"
" verified %s ago."), tmpstr);
xfree (tmpstr);
}
else
{
char *first_seen_ago_str = time_ago_str (now - first_seen);
xfree (first_done_ago_str);
}
/* TRANSLATORS: The final %s is replaced by a string like
"7 months, 1 day, 5 minutes, 0 seconds". */
es_fprintf (fp,
ngettext("Verified %ld message signed by \"%s\"\n"
"in the past %s.",
"Verified %ld messages signed by \"%s\"\n"
"in the past %s.",
messages),
messages, user_id, first_seen_ago_str);
if (opt.verbose)
{
es_fputs (" ", fp);
es_fputc ('(', fp);
es_fprintf (fp, _("policy: %s"), tofu_policy_str (policy));
es_fputs (")\n", fp);
}
else
es_fputs ("\n", fp);
if (messages > 1)
{
char *tmpstr = time_ago_str (now - most_recent_seen);
es_fputs (" ", fp);
es_fprintf (fp, _("The most recent message was"
" verified %s ago."), tmpstr);
xfree (tmpstr);
}
xfree (first_seen_ago_str);
if (opt.verbose)
{
es_fputs (" ", fp);
es_fputc ('(', fp);
es_fprintf (fp, _("policy: %s"), tofu_policy_str (policy));
es_fputs (")\n", fp);
}
else
es_fputs ("\n", fp);
}
{
char *tmpmsg, *p;
es_fputc (0, fp);
if (es_fclose_snatch (fp, (void **) &tmpmsg, NULL))
log_fatal ("error snatching memory stream\n");
msg = format_text (tmpmsg, 0, 72, 80);
es_free (tmpmsg);
{
char *tmpmsg, *p;
es_fputc (0, fp);
if (es_fclose_snatch (fp, (void **) &tmpmsg, NULL))
log_fatal ("error snatching memory stream\n");
msg = format_text (tmpmsg, 0, 72, 80);
es_free (tmpmsg);
/* Print a status line but suppress the trailing LF.
* Spaces are not percent escaped. */
if (*msg)
write_status_buffer (STATUS_TOFU_STATS_LONG,
msg, strlen (msg)-1, -1);
/* Print a status line but suppress the trailing LF.
* Spaces are not percent escaped. */
if (*msg)
write_status_buffer (STATUS_TOFU_STATS_LONG,
msg, strlen (msg)-1, -1);
/* Remove the non-breaking space markers. */
for (p=msg; *p; p++)
if (*p == '~')
*p = ' ';
}
/* Remove the non-breaking space markers. */
for (p=msg; *p; p++)
if (*p == '~')
*p = ' ';
log_string (GPGRT_LOG_INFO, msg);
xfree (msg);
}
if (policy == TOFU_POLICY_AUTO
/* Cf. write_stats_status */
&& (sqrtu32 (encryption_count) + sqrtu32 (signature_count)
< sqrtu32 (2 * BASIC_TRUST_THRESHOLD)))
{
char *set_policy_command;
char *text;
char *tmpmsg;
log_string (GPGRT_LOG_INFO, msg);
xfree (msg);
if (signature_count == 0)
log_info (_("Warning: we have yet to see"
" a message signed by this key and user id!\n"));
else if (signature_count == 1)
log_info (_("Warning: we've only seen a single message"
" signed by this key and user id!\n"));
if (policy == TOFU_POLICY_AUTO && messages < BASIC_TRUST_THRESHOLD)
{
char *set_policy_command;
char *text;
char *tmpmsg;
set_policy_command =
xasprintf ("gpg --tofu-policy bad %s", fingerprint);
if (messages == 0)
log_info (_("Warning: we have yet to see"
" a message signed by this key and user id!\n"));
else if (messages == 1)
log_info (_("Warning: we've only seen a single message"
" signed by this key and user id!\n"));
tmpmsg = xasprintf
(ngettext
("Warning: if you think you've seen more than %ld message "
"signed by this key and user id, then this key might be a "
"forgery! Carefully examine the email address for small "
"variations. If the key is suspect, then use\n"
" %s\n"
"to mark it as being bad.\n",
"Warning: if you think you've seen more than %ld messages "
"signed by this key, then this key might be a forgery! "
"Carefully examine the email address for small "
"variations. If the key is suspect, then use\n"
" %s\n"
"to mark it as being bad.\n",
signature_count),
signature_count, set_policy_command);
text = format_text (tmpmsg, 0, 72, 80);
xfree (tmpmsg);
log_string (GPGRT_LOG_INFO, text);
xfree (text);
set_policy_command =
xasprintf ("gpg --tofu-policy bad %s", fingerprint);
tmpmsg = xasprintf
(ngettext
("Warning: if you think you've seen more than %ld message "
"signed by this key and user id, then this key might be a "
"forgery! Carefully examine the email address for small "
"variations. If the key is suspect, then use\n"
" %s\n"
"to mark it as being bad.\n",
"Warning: if you think you've seen more than %ld messages "
"signed by this key, then this key might be a forgery! "
"Carefully examine the email address for small "
"variations. If the key is suspect, then use\n"
" %s\n"
"to mark it as being bad.\n",
messages),
messages, set_policy_command);
text = format_text (tmpmsg, 0, 72, 80);
xfree (tmpmsg);
log_string (GPGRT_LOG_INFO, text);
xfree (text);
es_free (set_policy_command);
}
}
es_free (set_policy_command);
}
}
out:
free_strlist (strlist);
xfree (fingerprint_pp);
return;
@ -2652,9 +2750,10 @@ email_from_user_id (const char *user_id)
This function returns 0 on success and an error code if an error
occured. */
gpg_error_t
tofu_register (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list,
const byte *sig_digest_bin, int sig_digest_bin_len,
time_t sig_time, const char *origin)
tofu_register_signature (ctrl_t ctrl,
PKT_public_key *pk, strlist_t user_id_list,
const byte *sig_digest_bin, int sig_digest_bin_len,
time_t sig_time, const char *origin)
{
gpg_error_t rc;
tofu_dbs_t dbs;
@ -2797,6 +2896,114 @@ tofu_register (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list,
return rc;
}
gpg_error_t
tofu_register_encryption (ctrl_t ctrl,
PKT_public_key *pk, strlist_t user_id_list,
int may_ask)
{
gpg_error_t rc = 0;
tofu_dbs_t dbs;
kbnode_t kb = NULL;
int free_user_id_list = 0;
char *fingerprint = NULL;
strlist_t user_id;
char *err = NULL;
dbs = opendbs (ctrl);
if (! dbs)
{
rc = gpg_error (GPG_ERR_GENERAL);
log_error (_("error opening TOFU database: %s\n"),
gpg_strerror (rc));
return rc;
}
/* Make sure PK is a primary key. */
if (keyid_cmp (pk_keyid (pk), pk->main_keyid) != 0
|| user_id_list)
kb = get_pubkeyblock (pk->keyid);
if (keyid_cmp (pk_keyid (pk), pk->main_keyid) != 0)
pk = kb->pkt->pkt.public_key;
if (! user_id_list)
{
/* Use all non-revoked user ids. Do use expired user ids. */
kbnode_t n = kb;
while ((n = find_next_kbnode (n, PKT_USER_ID)))
{
PKT_user_id *uid = n->pkt->pkt.user_id;
if (uid->is_revoked)
continue;
add_to_strlist (&user_id_list, uid->name);
}
free_user_id_list = 1;
if (! user_id_list)
log_info ("WARNING: Encrypting to %s, which has no"
"non-revoked user ids.\n",
keystr (pk->keyid));
}
fingerprint = hexfingerprint (pk, NULL, 0);
tofu_begin_batch_update (ctrl);
tofu_resume_batch_transaction (ctrl);
for (user_id = user_id_list; user_id; user_id = user_id->next)
{
char *email = email_from_user_id (user_id->d);
/* Make sure the binding exists and that we recognize any
conflicts. */
int tl = get_trust (ctrl, pk, fingerprint, email, user_id->d,
may_ask);
if (tl == _tofu_GET_TRUST_ERROR)
{
/* An error. */
xfree (email);
goto die;
}
rc = gpgsql_stepx
(dbs->db, &dbs->s.register_insert, NULL, NULL, &err,
"insert into encryptions\n"
" (binding, time)\n"
" values\n"
" ((select oid from bindings\n"
" where fingerprint = ? and email = ?),\n"
" strftime('%s', 'now'));",
GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email,
GPGSQL_ARG_END);
if (rc)
{
log_error (_("error updating TOFU database: %s\n"), err);
print_further_info ("insert encryption");
sqlite3_free (err);
}
xfree (email);
}
die:
tofu_end_batch_update (ctrl);
if (kb)
release_kbnode (kb);
if (free_user_id_list)
free_strlist (user_id_list);
xfree (fingerprint);
return rc;
}
/* Combine a trust level returned from the TOFU trust model with a
trust level returned by the PGP trust model. This is primarily of
interest when the trust model is tofu+pgp (TM_TOFU_PGP).

View File

@ -78,13 +78,24 @@ int tofu_policy_to_trust_level (enum tofu_policy policy);
data came from, e.g., "email:claws" (default: "unknown"). Note:
this function does not interact with the user, If there is a
conflict, or if the binding's policy is ask, the actual interaction
is deferred until tofu_get_validity is called.. Set the string
is deferred until tofu_get_validity is called. Set the string
list FLAG to indicate that a specified user id is expired. This
function returns 0 on success and an error code on failure. */
gpg_error_t tofu_register (ctrl_t ctrl, PKT_public_key *pk,
strlist_t user_id_list,
const byte *sigs_digest, int sigs_digest_len,
time_t sig_time, const char *origin);
gpg_error_t tofu_register_signature (ctrl_t ctrl, PKT_public_key *pk,
strlist_t user_id_list,
const byte *sigs_digest,
int sigs_digest_len,
time_t sig_time, const char *origin);
/* Note that an encrypted mail was sent to <PK, USER_ID>, for each
USER_ID in USER_ID_LIST. (If USER_ID_LIST is NULL, then all
non-revoked user ids associated with PK are used.) If MAY_ASK is
set, then may interact with the user to resolve a TOFU
conflict. */
gpg_error_t tofu_register_encryption (ctrl_t ctrl,
PKT_public_key *pk,
strlist_t user_id_list,
int may_ask);
/* Combine a trust level returned from the TOFU trust model with a
trust level returned by the PGP trust model. This is primarily of

View File

@ -1090,9 +1090,9 @@ tdb_get_validity_core (ctrl_t ctrl,
into account. */
if (sig)
{
err = tofu_register (ctrl, main_pk, user_id_list,
sig->digest, sig->digest_len,
sig->timestamp, "unknown");
err = tofu_register_signature (ctrl, main_pk, user_id_list,
sig->digest, sig->digest_len,
sig->timestamp, "unknown");
if (err)
{
log_error ("TOFU: error registering signature: %s\n",