gpg: Fix type mismatch resulting in a buffer overflow.
[gnupg.git] / g10 / tofu.c
index 06183ed..2433b7b 100644 (file)
@@ -167,56 +167,9 @@ tofu_cache_dump (struct db *db)
 #  define TIME_AGO_UNIT_LARGE_NAME _("month")
 #  define TIME_AGO_UNIT_LARGE_NAME_PLURAL _("months")
 #endif
-\f
-static char *
-fingerprint_str (const byte *fingerprint_bin)
-{
-  char *fingerprint = bin2hex (fingerprint_bin, MAX_FINGERPRINT_LEN, NULL);
-  if (! fingerprint)
-    log_fatal ("Out of memory.\n");
-  return fingerprint;
-}
-
-/* Pretty print a MAX_FINGERPRINT_LEN-byte binary fingerprint into a
-   malloc'd string.  */
-static char *
-fingerprint_format (const byte *fingerprint)
-{
-  char *fingerprint_pretty;
-  int space = (/* The characters and the NUL.  */
-              2 * MAX_FINGERPRINT_LEN + 1
-              /* After every fourth character, we add a space (except
-                 the last).  */
-              + 2 * MAX_FINGERPRINT_LEN / 4 - 1
-              /* Half way through we add a second space.  */
-              + 1);
-  int i;
-  int j;
-
-  if (strlen (fingerprint) != 2 * MAX_FINGERPRINT_LEN)
-    {
-      log_info (_("Fingerprint with unexpected length (%zd chars)\n"),
-                strlen (fingerprint));
-      return xstrdup (fingerprint);
-    }
 
-  fingerprint_pretty = xmalloc (space);
-
-  for (i = 0, j = 0; i < MAX_FINGERPRINT_LEN * 2; i ++)
-    {
-      if (i && i % 4 == 0)
-       fingerprint_pretty[j ++] = ' ';
-      if (i == MAX_FINGERPRINT_LEN * 2 / 2)
-       fingerprint_pretty[j ++] = ' ';
-
-      fingerprint_pretty[j ++] = fingerprint[i];
-    }
-  fingerprint_pretty[j ++] = 0;
-  assert (j == space);
-
-  return fingerprint_pretty;
-}
 \f
+
 const char *
 tofu_policy_str (enum tofu_policy policy)
 {
@@ -620,7 +573,7 @@ initdb (sqlite3 *db, enum db_type type)
          latter binding, we warn the user about the conflict and ask
          for a policy decision about the new binding.  We also change
          the old binding's policy to ask if it was auto.  So that we
-         know why this occured, we also set conflict to 0xbaddecaf.
+         know why this occurred, we also set conflict to 0xbaddecaf.
   */
   if (type == DB_EMAIL || type == DB_COMBINED)
     rc = sqlite3_exec_printf
@@ -995,7 +948,8 @@ opendbs (void)
 
       if (have_tofu_db && have_tofu_d)
        {
-         log_info (_("Warning: Home directory contains both tofu.db and tofu.d.  Using split format for TOFU DB.\n"));
+         log_info (_("Warning: Home directory contains both tofu.db"
+                      " and tofu.d.  Using split format for TOFU DB.\n"));
          opt.tofu_db_format = TOFU_DB_SPLIT;
        }
       else if (have_tofu_db)
@@ -1012,9 +966,9 @@ opendbs (void)
        }
       else
        {
-         opt.tofu_db_format = TOFU_DB_SPLIT;
+         opt.tofu_db_format = TOFU_DB_FLAT;
          if (DBG_TRUST)
-           log_debug ("Using split format for TOFU DB.\n");
+           log_debug ("Using flat format for TOFU DB.\n");
        }
     }
 
@@ -1121,11 +1075,14 @@ static gpg_error_t
 record_binding (struct dbs *dbs, const char *fingerprint, const char *email,
                const char *user_id, enum tofu_policy policy, int show_old)
 {
-  char *fingerprint_pp = fingerprint_format (fingerprint);
+  char *fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0);
   struct db *db_email = NULL, *db_key = NULL;
   int rc;
   char *err = NULL;
-  enum tofu_policy policy_old = TOFU_POLICY_NONE;
+  /* policy_old needs to be a long and not an enum tofu_policy,
+     because we pass it by reference to get_single_long_cb2, which
+     expects a long.  */
+  long policy_old = TOFU_POLICY_NONE;
 
   if (! (policy == TOFU_POLICY_AUTO
         || policy == TOFU_POLICY_GOOD
@@ -1646,44 +1603,56 @@ get_trust (struct dbs *dbs, const char *fingerprint, const char *email,
   if (! db)
     return _tofu_GET_TRUST_ERROR;
 
-  fingerprint_pp = fingerprint_format (fingerprint);
+  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.  */
     {
-      const char *keyid;
-      KEYDB_SEARCH_DESC desc;
-
-      /* We need to convert the fingerprint as a string to a long
-         keyid.
-
-         FINGERPRINT is stored as follows:
+      PKT_public_key *pk;
+      u32 kid[2];
+      char fpr_bin[MAX_FINGERPRINT_LEN+1];
+      size_t fpr_bin_len;
 
-           362D3527F53AAD1971AAFDE658859975EE37CF96
-                                -------------------
-
-         The last 16 characters are the long keyid.
-      */
-      assert (strlen (fingerprint) > 4 * 4);
-      keyid = &fingerprint[strlen (fingerprint) - 16];
+      if (!hex2str (fingerprint, fpr_bin, sizeof fpr_bin, &fpr_bin_len))
+        {
+          log_error ("error converting fingerprint: %s\n",
+                     gpg_strerror (gpg_error_from_syserror ()));
+          return _tofu_GET_TRUST_ERROR;
+        }
 
-      rc = classify_user_id (keyid, &desc, 1);
-      if (rc || desc.mode != KEYDB_SEARCH_MODE_LONG_KID)
+      /* We need to lookup the key by fingerprint again so that we can
+         properly extract the keyid.  Extracting direct from the
+         fingerprint works only for v4 keys and would assume that
+         there is no collision in the low 64 bit.  We can't guarantee
+         the latter in case the Tofu DB is used with a different
+         keyring.  In any case the UTK stuff needs to be changed to
+         use only fingerprints.  */
+      pk = xtrycalloc (1, sizeof *pk);
+      if (!pk)
+         {
+           log_error (_("out of core\n"));
+           return _tofu_GET_TRUST_ERROR;
+         }
+      rc = get_pubkey_byfprint_fast (pk, fpr_bin, fpr_bin_len);
+      if (rc)
         {
-          log_error (_("'%s' is not a valid long keyID\n"), keyid);
-          goto out;
+          log_error (_("public key %s not found: %s\n"),
+                     fingerprint, gpg_strerror (rc));
+          return _tofu_GET_TRUST_ERROR;
         }
+      keyid_from_pk (pk, kid);
+      free_public_key (pk);
 
-      if (tdb_keyid_is_utk (desc.u.kid))
+      if (tdb_keyid_is_utk (kid))
         {
           if (policy == TOFU_POLICY_NONE)
             {
               if (record_binding (dbs, fingerprint, email, user_id,
                                   TOFU_POLICY_AUTO, 0) != 0)
                 {
-                  log_error (_("error setting TOFU binding's trust level to %s\n"),
-                             "auto");
+                  log_error (_("error setting TOFU binding's trust level"
+                               " to %s\n"), "auto");
                   trust_level = _tofu_GET_TRUST_ERROR;
                   goto out;
                 }
@@ -1873,7 +1842,7 @@ get_trust (struct dbs *dbs, const char *fingerprint, const char *email,
                display this message.  */
             && conflict && strcmp (conflict, fingerprint) != 0)
       {
-        char *conflict_pp = fingerprint_format (conflict);
+        char *conflict_pp = format_hexfingerprint (conflict, NULL, 0);
        es_fprintf (fp,
                    _("The key %s raised a conflict with this binding (%s)."
                       "  Since this binding's policy was 'auto', it was "
@@ -2030,7 +1999,7 @@ get_trust (struct dbs *dbs, const char *fingerprint, const char *email,
                 char *key_pp;
                key = stats_iter->fingerprint;
                this_key = strcmp (key, fingerprint) == 0;
-                key_pp = fingerprint_format (key);
+                key_pp = format_hexfingerprint (key, NULL, 0);
                if (this_key)
                  es_fprintf (fp, _("  %s (this key):"), key_pp);
                else
@@ -2072,7 +2041,9 @@ get_trust (struct dbs *dbs, const char *fingerprint, const char *email,
            "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.";
+        text = format_text (text, 0, 72, 80);
        es_fprintf (fp, "\n%s\n", text);
+        xfree (text);
       }
 
     es_fputc ('\n', fp);
@@ -2248,7 +2219,8 @@ time_ago_str (long long int t)
 
   fp = es_fopenmem (0, "rw,samethread");
   if (! fp)
-    log_fatal ("error creating memory stream\n");
+    log_fatal ("error creating memory stream: %s\n",
+               gpg_strerror (gpg_error_from_syserror()));
 
   if (years)
     {
@@ -2263,7 +2235,7 @@ time_ago_str (long long int t)
   if ((first == -1 || i - first <= 3) && months)
     {
       if (count)
-        es_fprintf (fp, _(", "));
+        es_fprintf (fp, ", ");
 
       if (months > 1)
         es_fprintf (fp, _("%d months"), months);
@@ -2276,7 +2248,7 @@ time_ago_str (long long int t)
   if ((first == -1 || i - first <= 3) && count < 2 && days)
     {
       if (count)
-        es_fprintf (fp, _(", "));
+        es_fprintf (fp, ", ");
 
       if (days > 1)
         es_fprintf (fp, _("%d days"), days);
@@ -2289,7 +2261,7 @@ time_ago_str (long long int t)
   if ((first == -1 || i - first <= 3) && count < 2 && hours)
     {
       if (count)
-        es_fprintf (fp, _(", "));
+        es_fprintf (fp, ", ");
 
       if (hours > 1)
         es_fprintf (fp, _("%d hours"), hours);
@@ -2302,7 +2274,7 @@ time_ago_str (long long int t)
   if ((first == -1 || i - first <= 3) && count < 2 && minutes)
     {
       if (count)
-        es_fprintf (fp, _(", "));
+        es_fprintf (fp, ", ");
 
       if (minutes > 1)
         es_fprintf (fp, _("%d minutes"), minutes);
@@ -2315,7 +2287,7 @@ time_ago_str (long long int t)
   if ((first == -1 || i - first <= 3) && count < 2)
     {
       if (count)
-        es_fprintf (fp, _(", "));
+        es_fprintf (fp, ", ");
 
       if (seconds > 1)
         es_fprintf (fp, _("%d seconds"), seconds);
@@ -2345,7 +2317,7 @@ show_statistics (struct dbs *dbs, const char *fingerprint,
   if (! db)
     return;
 
-  fingerprint_pp = fingerprint_format (fingerprint);
+  fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0);
 
   rc = sqlite3_exec_printf
     (db->db, strings_collect_cb, &strlist, &err,
@@ -2377,6 +2349,7 @@ show_statistics (struct dbs *dbs, const char *fingerprint,
       char *tail = NULL;
       signed long messages;
       signed long first_seen_ago;
+      signed long most_recent_seen_ago;
 
       assert (strlist_length (strlist) == 3);
 
@@ -2392,7 +2365,10 @@ show_statistics (struct dbs *dbs, const char *fingerprint,
 
       if (messages == 0 && *strlist->next->d == '\0')
        /* min(NULL) => NULL => "".  */
-       first_seen_ago = -1;
+        {
+          first_seen_ago = -1;
+          most_recent_seen_ago = -1;
+        }
       else
        {
          errno = 0;
@@ -2405,10 +2381,22 @@ show_statistics (struct dbs *dbs, const char *fingerprint,
                         strlist->next->d, strerror (errno));
              first_seen_ago = 0;
            }
+
+         errno = 0;
+         most_recent_seen_ago = strtol (strlist->next->next->d, &tail, 0);
+         if (errno || *tail != '\0')
+           /* Abort.  */
+           {
+             log_debug ("%s:%d: Couldn't convert %s (most_recent_seen) to an int: %s.\n",
+                        __func__, __LINE__,
+                        strlist->next->next->d, strerror (errno));
+             most_recent_seen_ago = 0;
+           }
        }
 
       if (messages == -1 || first_seen_ago == 0)
-        log_info (_("Failed to collect signature statistics for \"%s\" (key %s)\n"),
+        log_info (_("Failed to collect signature statistics"
+                    " for \"%s\" (key %s)\n"),
                   user_id, fingerprint_pp);
       else
        {
@@ -2428,6 +2416,8 @@ show_statistics (struct dbs *dbs, const char *fingerprint,
          else
            {
               char *first_seen_ago_str = time_ago_str (first_seen_ago);
+              char *most_recent_seen_ago_str =
+                time_ago_str (most_recent_seen_ago);
 
              es_fprintf (fp,
                          _("Verified %ld messages signed by \"%s\""
@@ -2436,8 +2426,13 @@ show_statistics (struct dbs *dbs, const char *fingerprint,
                          fingerprint_pp, tofu_policy_str (policy),
                           first_seen_ago_str);
 
+              if (messages > 1)
+                es_fprintf (fp,
+                            _("  The most recent message was verified %s ago."),
+                            most_recent_seen_ago_str);
 
               xfree (first_seen_ago_str);
+              xfree (most_recent_seen_ago_str);
             }
 
          es_fputc (0, fp);
@@ -2450,12 +2445,15 @@ show_statistics (struct dbs *dbs, const char *fingerprint,
          if (policy == TOFU_POLICY_AUTO && messages < 10)
            {
              char *set_policy_command;
-             const char *text;
+             char *text;
+              char *tmp;
 
              if (messages == 0)
-               log_info (_("Warning: we've have yet to see a message signed by this key!\n"));
+               log_info (_("Warning: we've have yet to see"
+                            " a message signed by this key!\n"));
              else if (messages == 1)
-               log_info (_("Warning: we've only seen a single message signed by this key!\n"));
+               log_info (_("Warning: we've only seen a"
+                            " single message signed by this key!\n"));
 
              set_policy_command =
                xasprintf ("gpg --tofu-policy bad \"%s\"", fingerprint);
@@ -2470,9 +2468,14 @@ show_statistics (struct dbs *dbs, const char *fingerprint,
                  "Carefully examine the email address for small variations "
                  "(e.g., additional white space).  If the key is suspect, "
                  "then use '%s' to mark it as being bad.\n";
-             log_info (text,
-                       messages, messages == 1 ? _("message") : _("message"),
-                       set_policy_command);
+              tmp = xasprintf
+                (text,
+                 messages, messages == 1 ? _("message") : _("message"),
+                 set_policy_command);
+              text = format_text (tmp, 0, 72, 80);
+              xfree (tmp);
+             log_info ("%s", text);
+              xfree (text);
              free (set_policy_command);
            }
        }
@@ -2493,16 +2496,18 @@ email_from_user_id (const char *user_id)
 {
   char *email = mailbox_from_userid (user_id);
   if (! email)
-    /* Hmm, no email address was provided.  Just take the lower-case
-       version of the whole user id.  It could be a hostname, for
-       instance.  */
-    email = ascii_strlwr (xstrdup (user_id));
+    {
+      /* Hmm, no email address was provided or we are out of core.  Just
+         take the lower-case version of the whole user id.  It could be
+         a hostname, for instance.  */
+      email = ascii_strlwr (xstrdup (user_id));
+    }
 
   return email;
 }
 
-/* Register the signature with the binding <FINGERPRINT_BIN, USER_ID>.
-   FINGERPRINT must be MAX_FINGERPRINT_LEN bytes long.
+/* Register the signature with the binding <fingerprint, USER_ID>.
+   The fingerprint is taken from the primary key packet PK.
 
    SIG_DIGEST_BIN is the binary representation of the message's
    digest.  SIG_DIGEST_BIN_LEN is its length.
@@ -2521,7 +2526,7 @@ email_from_user_id (const char *user_id)
    This function returns the binding's trust level on return.  If an
    error occurs, this function returns TRUST_UNKNOWN.  */
 int
-tofu_register (const byte *fingerprint_bin, const char *user_id,
+tofu_register (PKT_public_key *pk, const char *user_id,
               const byte *sig_digest_bin, int sig_digest_bin_len,
               time_t sig_time, const char *origin, int may_ask)
 {
@@ -2546,8 +2551,8 @@ tofu_register (const byte *fingerprint_bin, const char *user_id,
       goto die;
     }
 
-  fingerprint = fingerprint_str (fingerprint_bin);
-  fingerprint_pp = fingerprint_format (fingerprint);
+  fingerprint = hexfingerprint (pk, NULL, 0);
+  fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0);
 
   if (! *user_id)
     {
@@ -2738,7 +2743,7 @@ tofu_wot_trust_combine (int tofu_base, int wot_base)
 /* Return the validity (TRUST_NEVER, etc.) of the binding
    <FINGERPRINT, USER_ID>.
 
-   FINGERPRINT must be a MAX_FINGERPRINT_LEN-byte fingerprint.
+   PK is the primary key packet.
 
    If MAY_ASK is 1 and the policy is TOFU_POLICY_ASK, then the user
    will be prompted to choose a different policy.  If MAY_ASK is 0 and
@@ -2746,7 +2751,7 @@ tofu_wot_trust_combine (int tofu_base, int wot_base)
 
    Returns TRUST_UNDEFINED if an error occurs.  */
 int
-tofu_get_validity (const byte *fingerprint_bin, const char *user_id,
+tofu_get_validity (PKT_public_key *pk, const char *user_id,
                   int may_ask)
 {
   struct dbs *dbs;
@@ -2761,11 +2766,12 @@ tofu_get_validity (const byte *fingerprint_bin, const char *user_id,
       goto die;
     }
 
-  fingerprint = fingerprint_str (fingerprint_bin);
+  fingerprint = hexfingerprint (pk, NULL, 0);
 
   if (! *user_id)
     {
-      log_debug ("user id is empty.  Can't get TOFU validity for this binding.\n");
+      log_debug ("user id is empty."
+                 "  Can't get TOFU validity for this binding.\n");
       goto die;
     }
 
@@ -2800,8 +2806,6 @@ tofu_set_policy (kbnode_t kb, enum tofu_policy policy)
 {
   struct dbs *dbs;
   PKT_public_key *pk;
-  char fingerprint_bin[MAX_FINGERPRINT_LEN];
-  size_t fingerprint_bin_len = sizeof (fingerprint_bin);
   char *fingerprint = NULL;
 
   assert (kb->pkt->pkttype == PKT_PUBLIC_KEY);
@@ -2821,10 +2825,7 @@ tofu_set_policy (kbnode_t kb, enum tofu_policy policy)
         && pk->main_keyid[1] == pk->keyid[1]))
     log_bug ("%s: Passed a subkey, but expecting a primary key.\n", __func__);
 
-  fingerprint_from_pk (pk, fingerprint_bin, &fingerprint_bin_len);
-  assert (fingerprint_bin_len == sizeof (fingerprint_bin));
-
-  fingerprint = fingerprint_str (fingerprint_bin);
+  fingerprint = hexfingerprint (pk, NULL, 0);
 
   for (; kb; kb = kb->next)
     {
@@ -2882,8 +2883,6 @@ tofu_get_policy (PKT_public_key *pk, PKT_user_id *user_id,
                 enum tofu_policy *policy)
 {
   struct dbs *dbs;
-  char fingerprint_bin[MAX_FINGERPRINT_LEN];
-  size_t fingerprint_bin_len = sizeof (fingerprint_bin);
   char *fingerprint;
   char *email;
 
@@ -2898,10 +2897,7 @@ tofu_get_policy (PKT_public_key *pk, PKT_user_id *user_id,
       return gpg_error (GPG_ERR_GENERAL);
     }
 
-  fingerprint_from_pk (pk, fingerprint_bin, &fingerprint_bin_len);
-  assert (fingerprint_bin_len == sizeof (fingerprint_bin));
-
-  fingerprint = fingerprint_str (fingerprint_bin);
+  fingerprint = hexfingerprint (pk, NULL, 0);
 
   email = email_from_user_id (user_id->name);