gpg: Print a warning on Tor problems.
[gnupg.git] / g10 / tofu.c
index d095bce..8d535fa 100644 (file)
 
 #define CONTROL_L ('L' - 'A' + 1)
 
-/* Number of signed messages required to indicate that enough history
- * is available for basic trust.  */
-#define BASIC_TRUST_THRESHOLD  10
-/* Number of signed messages required to indicate that a lot of
- * history is available.  */
-#define FULL_TRUST_THRESHOLD  100
+/* Number of days with signed / ecnrypted messages required to
+ * indicate that enough history is available for basic trust.  */
+#define BASIC_TRUST_THRESHOLD  4
+/* Number of days with signed / encrypted messages required to
+ * indicate that a lot of history is available.  */
+#define FULL_TRUST_THRESHOLD  21
 
 
 /* A struct with data pertaining to the tofu DB.  There is one such
@@ -112,8 +112,10 @@ struct tofu_dbs_s
 /* Local prototypes.  */
 static gpg_error_t end_transaction (ctrl_t ctrl, int only_batch);
 static char *email_from_user_id (const char *user_id);
-
-
+static int show_statistics (tofu_dbs_t dbs,
+                            const char *fingerprint, const char *email,
+                            enum tofu_policy policy,
+                            estream_t outfp, int only_status_fd, time_t now);
 \f
 const char *
 tofu_policy_str (enum tofu_policy policy)
@@ -1797,6 +1799,9 @@ ask_about_binding (ctrl_t ctrl,
               xfree (key_pp);
 
               seen_in_past = 0;
+
+              show_statistics (dbs, stats_iter->fingerprint, email,
+                               TOFU_POLICY_ASK, NULL, 1, now);
             }
 
           if (labs(stats_iter->time_ago) == 1)
@@ -2230,7 +2235,7 @@ build_conflict_set (tofu_dbs_t dbs,
     int *die;
 
     log_assert (conflict_set_count > 0);
-    die = xtrycalloc (1, conflict_set_count);
+    die = xtrycalloc (conflict_set_count, sizeof *die);
     if (!die)
       {
         /*err = gpg_error_from_syserror ();*/
@@ -2452,16 +2457,16 @@ get_policy (tofu_dbs_t dbs, PKT_public_key *pk,
   /* See if the key is signed by an ultimately trusted key.  */
   {
     int fingerprint_raw_len = strlen (fingerprint) / 2;
-    char fingerprint_raw[fingerprint_raw_len];
+    char fingerprint_raw[20];
     int len = 0;
 
-    if (fingerprint_raw_len != 20
+    if (fingerprint_raw_len != sizeof fingerprint_raw
         || ((len = hex2bin (fingerprint,
                             fingerprint_raw, fingerprint_raw_len))
             != strlen (fingerprint)))
       {
         if (DBG_TRUST)
-          log_debug ("TOFU: Bad fingerprint: %s (len: %zd, parsed: %d)\n",
+          log_debug ("TOFU: Bad fingerprint: %s (len: %zu, parsed: %d)\n",
                      fingerprint, strlen (fingerprint), len);
       }
     else
@@ -2752,7 +2757,13 @@ get_trust (ctrl_t ctrl, PKT_public_key *pk,
                          now);
     }
   else
-    trust_level = TRUST_UNDEFINED;
+    {
+      for (iter = conflict_set; iter; iter = iter->next)
+        show_statistics (dbs, iter->d, email,
+                         TOFU_POLICY_ASK, NULL, 1, now);
+
+      trust_level = TRUST_UNDEFINED;
+    }
 
   /* Mark any conflicting bindings that have an automatic policy as
    * now requiring confirmation.  Note: we do this after we ask for
@@ -2864,25 +2875,27 @@ write_stats_status (estream_t fp,
                     unsigned long signature_count,
                     unsigned long signature_first_seen,
                     unsigned long signature_most_recent,
+                    unsigned long signature_days,
                     unsigned long encryption_count,
                     unsigned long encryption_first_done,
-                    unsigned long encryption_most_recent)
+                    unsigned long encryption_most_recent,
+                    unsigned long encryption_days)
 {
   int summary;
   int validity;
-  unsigned long messages;
+  unsigned long days;
 
   /* Use the euclidean distance (m = sqrt(a^2 + b^2)) rather then the
      sum of the magnitudes (m = a + b) to ensure a balance between
      verified signatures and encrypted messages.  */
-  messages = sqrtu32 (signature_count * signature_count
-                      + encryption_count * encryption_count);
+  days = sqrtu32 (signature_days * signature_days
+                  + encryption_days * encryption_days);
 
-  if (messages < 1)
+  if (days < 1)
     validity = 1; /* Key without history.  */
-  else if (messages < 2 * BASIC_TRUST_THRESHOLD)
+  else if (days < 2 * BASIC_TRUST_THRESHOLD)
     validity = 2; /* Key with too little history.  */
-  else if (messages < 2 * FULL_TRUST_THRESHOLD)
+  else if (days < 2 * FULL_TRUST_THRESHOLD)
     validity = 3; /* Key with enough history for basic trust.  */
   else
     validity = 4; /* Key with a lot of history.  */
@@ -2894,17 +2907,17 @@ write_stats_status (estream_t fp,
 
   if (fp)
     {
-      es_fprintf (fp, "tfs:1:%d:%lu:%lu:%s:%lu:%lu:%lu:%lu:%d:\n",
+      es_fprintf (fp, "tfs:1:%d:%lu:%lu:%s:%lu:%lu:%lu:%lu:%d:%lu:%lu:\n",
                   summary, signature_count, encryption_count,
                   tofu_policy_str (policy),
                   signature_first_seen, signature_most_recent,
                   encryption_first_done, encryption_most_recent,
-                  validity);
+                  validity, signature_days, encryption_days);
     }
   else
     {
       write_status_printf (STATUS_TOFU_STATS,
-                           "%d %lu %lu %s %lu %lu %lu %lu %d",
+                           "%d %lu %lu %s %lu %lu %lu %lu %d %lu %lu",
                            summary,
                            signature_count,
                            encryption_count,
@@ -2913,24 +2926,26 @@ write_stats_status (estream_t fp,
                            signature_most_recent,
                            encryption_first_done,
                            encryption_most_recent,
-                           validity);
+                           validity,
+                           signature_days, encryption_days);
     }
 }
 
 /* Note: If OUTFP is not NULL, this function merely prints a "tfs" record
  * to OUTFP.
  *
- * Returns whether the caller should call show_warning after iterating
- * over all user ids.
+ * POLICY is the key's policy (as returned by get_policy).
+ *
+ * Returns 0 if if ONLY_STATUS_FD is set.  Otherwise, returns whether
+ * the caller should call show_warning after iterating over all user
+ * ids.
  */
 static int
-show_statistics (tofu_dbs_t dbs, PKT_public_key *pk, const char *fingerprint,
-                const char *email, const char *user_id,
-                estream_t outfp, time_t now)
+show_statistics (tofu_dbs_t dbs,
+                 const char *fingerprint, const char *email,
+                 enum tofu_policy policy,
+                estream_t outfp, int only_status_fd, time_t now)
 {
-  enum tofu_policy policy =
-    get_policy (dbs, pk, fingerprint, user_id, email, NULL, now);
-
   char *fingerprint_pp;
   int rc;
   strlist_t strlist = NULL;
@@ -2939,13 +2954,16 @@ show_statistics (tofu_dbs_t dbs, PKT_public_key *pk, const char *fingerprint,
   unsigned long signature_first_seen = 0;
   unsigned long signature_most_recent = 0;
   unsigned long signature_count = 0;
+  unsigned long signature_days = 0;
   unsigned long encryption_first_done = 0;
   unsigned long encryption_most_recent = 0;
   unsigned long encryption_count = 0;
+  unsigned long encryption_days = 0;
 
   int show_warning = 0;
 
-  (void) user_id;
+  if (only_status_fd && ! is_status_enabled ())
+    return 0;
 
   fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0);
 
@@ -2965,18 +2983,38 @@ show_statistics (tofu_dbs_t dbs, PKT_public_key *pk, const char *fingerprint,
       rc = gpg_error (GPG_ERR_GENERAL);
       goto out;
     }
+  rc = gpgsql_exec_printf
+    (dbs->db, strings_collect_cb, &strlist, &err,
+     "select count (*) from\n"
+     "  (select round(signatures.time / (24 * 60 * 60)) day\n"
+     "    from signatures\n"
+     "    left join bindings on signatures.binding = bindings.oid\n"
+     "    where fingerprint = %Q and email = %Q\n"
+     "    group by day);",
+     fingerprint, email);
+  if (rc)
+    {
+      log_error (_("error reading TOFU database: %s\n"), err);
+      print_further_info ("getting signature statistics (by day)");
+      sqlite3_free (err);
+      rc = gpg_error (GPG_ERR_GENERAL);
+      goto out;
+    }
 
   if (strlist)
     {
-      /* We expect exactly 3 elements.  */
+      /* We expect exactly 4 elements.  */
       log_assert (strlist->next);
       log_assert (strlist->next->next);
-      log_assert (! strlist->next->next->next);
+      log_assert (strlist->next->next->next);
+      log_assert (! strlist->next->next->next->next);
 
-      string_to_ulong (&signature_count, strlist->d, -1, __LINE__);
-      string_to_ulong (&signature_first_seen, strlist->next->d, -1, __LINE__);
-      string_to_ulong (&signature_most_recent,
+      string_to_ulong (&signature_days, strlist->d, -1, __LINE__);
+      string_to_ulong (&signature_count, strlist->next->d, -1, __LINE__);
+      string_to_ulong (&signature_first_seen,
                        strlist->next->next->d, -1, __LINE__);
+      string_to_ulong (&signature_most_recent,
+                       strlist->next->next->next->d, -1, __LINE__);
 
       free_strlist (strlist);
       strlist = NULL;
@@ -2998,18 +3036,38 @@ show_statistics (tofu_dbs_t dbs, PKT_public_key *pk, const char *fingerprint,
       rc = gpg_error (GPG_ERR_GENERAL);
       goto out;
     }
+  rc = gpgsql_exec_printf
+    (dbs->db, strings_collect_cb, &strlist, &err,
+     "select count (*) from\n"
+     "  (select round(encryptions.time / (24 * 60 * 60)) day\n"
+     "    from encryptions\n"
+     "    left join bindings on encryptions.binding = bindings.oid\n"
+     "    where fingerprint = %Q and email = %Q\n"
+     "    group by day);",
+     fingerprint, email);
+  if (rc)
+    {
+      log_error (_("error reading TOFU database: %s\n"), err);
+      print_further_info ("getting encryption statistics (by day)");
+      sqlite3_free (err);
+      rc = gpg_error (GPG_ERR_GENERAL);
+      goto out;
+    }
 
   if (strlist)
     {
-      /* We expect exactly 3 elements.  */
+      /* We expect exactly 4 elements.  */
       log_assert (strlist->next);
       log_assert (strlist->next->next);
-      log_assert (! strlist->next->next->next);
+      log_assert (strlist->next->next->next);
+      log_assert (! strlist->next->next->next->next);
 
-      string_to_ulong (&encryption_count, strlist->d, -1, __LINE__);
-      string_to_ulong (&encryption_first_done, strlist->next->d, -1, __LINE__);
-      string_to_ulong (&encryption_most_recent,
+      string_to_ulong (&encryption_days, strlist->d, -1, __LINE__);
+      string_to_ulong (&encryption_count, strlist->next->d, -1, __LINE__);
+      string_to_ulong (&encryption_first_done,
                        strlist->next->next->d, -1, __LINE__);
+      string_to_ulong (&encryption_most_recent,
+                       strlist->next->next->next->d, -1, __LINE__);
 
       free_strlist (strlist);
       strlist = NULL;
@@ -3023,11 +3081,13 @@ show_statistics (tofu_dbs_t dbs, PKT_public_key *pk, const char *fingerprint,
                       signature_count,
                       signature_first_seen,
                       signature_most_recent,
+                      signature_days,
                       encryption_count,
                       encryption_first_done,
-                      encryption_most_recent);
+                      encryption_most_recent,
+                      encryption_days);
 
-  if (!outfp)
+  if (!outfp && !only_status_fd)
     {
       estream_t fp;
       char *msg;
@@ -3419,7 +3479,7 @@ tofu_register_encryption (ctrl_t ctrl,
       free_user_id_list = 1;
 
       if (! user_id_list)
-        log_info (_("WARNING: Encrypting to %s, which has no"
+        log_info (_("WARNING: Encrypting to %s, which has no "
                     "non-revoked user ids.\n"),
                   keystr (pk->keyid));
     }
@@ -3555,6 +3615,7 @@ tofu_write_tfs_record (ctrl_t ctrl, estream_t fp,
   tofu_dbs_t dbs;
   char *fingerprint;
   char *email;
+  enum tofu_policy policy;
 
   if (!*user_id)
     return 0;  /* No TOFU stats possible for an empty ID.  */
@@ -3569,8 +3630,9 @@ tofu_write_tfs_record (ctrl_t ctrl, estream_t fp,
 
   fingerprint = hexfingerprint (pk, NULL, 0);
   email = email_from_user_id (user_id);
+  policy = get_policy (dbs, pk, fingerprint, user_id, email, NULL, now);
 
-  show_statistics (dbs, pk, fingerprint, email, user_id, fp, now);
+  show_statistics (dbs, fingerprint, email, policy, fp, 0, now);
 
   xfree (email);
   xfree (fingerprint);
@@ -3645,8 +3707,13 @@ tofu_get_validity (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list,
         bindings_valid ++;
 
       if (may_ask && tl != TRUST_ULTIMATE && tl != TRUST_EXPIRED)
-        need_warning |=
-          show_statistics (dbs, pk, fingerprint, email, user_id->d, NULL, now);
+        {
+          enum tofu_policy policy =
+            get_policy (dbs, pk, fingerprint, user_id->d, email, NULL, now);
+
+          need_warning |=
+            show_statistics (dbs, fingerprint, email, policy, NULL, 0, now);
+        }
 
       if (tl == TRUST_NEVER)
         trust_level = TRUST_NEVER;