mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-30 16:17:02 +01:00
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:
parent
a9e6db6c7e
commit
875ac9216f
16
doc/DETAILS
16
doc/DETAILS
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 )
|
||||
|
635
g10/tofu.c
635
g10/tofu.c
@ -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).
|
||||
|
21
g10/tofu.h
21
g10/tofu.h
@ -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
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user