gpg: Check for ambiguous or non-matching key specs.
authorNeal H. Walfield <neal@g10code.com>
Thu, 5 Nov 2015 16:29:53 +0000 (17:29 +0100)
committerNeal H. Walfield <neal@g10code.com>
Fri, 6 Nov 2015 11:04:35 +0000 (12:04 +0100)
* g10/gpg.c (check_user_ids): New function.
(main): Check that any user id specifications passed to --local-user
and --remote-user correspond to exactly 1 user.  Check that any user
id specifications passed to --default-key correspond to at most 1
user.  Warn if any user id specifications passed to --local-user or
--default-user are possible ambiguous (are not specified by long keyid
or fingerprint).
* g10/getkey.c (parse_def_secret_key): Don't warn about possible
ambiguous key descriptions here.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
GnuPG-bug-id: 1128
Debian-debug-id: 544490

g10/getkey.c
g10/gpg.c

index b4086a2..9e123ee 100644 (file)
@@ -1142,15 +1142,6 @@ parse_def_secret_key (ctrl_t ctrl)
           continue;
         }
 
-      if (! (desc.mode == KEYDB_SEARCH_MODE_LONG_KID
-             || desc.mode == KEYDB_SEARCH_MODE_FPR16
-             || desc.mode == KEYDB_SEARCH_MODE_FPR20
-             || desc.mode == KEYDB_SEARCH_MODE_FPR)
-          && ! warned)
-        log_info (_("Warning: value '%s' for --default-key"
-                    " should be a long keyid or a fingerprint.\n"),
-                  t->d);
-
       if (! hd)
         hd = keydb_new ();
       else
index ef283b4..b15be91 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -2081,6 +2081,159 @@ get_default_configname (void)
   return configname;
 }
 
+gpg_error_t
+check_user_ids (strlist_t *sp,
+                int warn_possibly_ambiguous,
+                int error_if_not_found)
+{
+  strlist_t s = *sp;
+  strlist_t s2 = NULL;
+  strlist_t t;
+
+  gpg_error_t rc = 0;
+  gpg_error_t err;
+
+  KEYDB_HANDLE hd = NULL;
+
+  if (! s)
+    return 0;
+
+  for (t = s; t; t = t->next)
+    {
+      const char *option;
+
+      KEYDB_SEARCH_DESC desc;
+      KBNODE kb;
+      PKT_public_key *pk;
+      char fingerprint_bin[MAX_FINGERPRINT_LEN];
+      size_t fingerprint_bin_len = sizeof (fingerprint_bin);
+      char fingerprint[2 * MAX_FINGERPRINT_LEN + 1];
+
+
+      switch (t->flags >> 2)
+        {
+        case oDefaultKey: option = "--default-key"; break;
+        case oEncryptTo: option = "--encrypt-to"; break;
+        case oHiddenEncryptTo: option = "--hidden-encrypt-to"; break;
+        case oEncryptToDefaultKey: option = "--encrypt-to-default-key"; break;
+        case oRecipient: option = "--recipient"; break;
+        case oHiddenRecipient: option = "--hidden-recipient"; break;
+        case oLocalUser: option = "--local-user"; break;
+        default: log_bug ("Unsupport option: %d\n", t->flags >> 2);
+        }
+
+      err = classify_user_id (t->d, &desc, 1);
+      if (err)
+        {
+          if (! rc)
+            rc = err;
+
+          log_error (_("Invalid value ('%s')."), t->d);
+          if (!opt.quiet)
+            log_info (_("(check argument of option '%s')\n"), option);
+          continue;
+        }
+
+      if (warn_possibly_ambiguous
+          && ! (desc.mode == KEYDB_SEARCH_MODE_LONG_KID
+                || desc.mode == KEYDB_SEARCH_MODE_FPR16
+                || desc.mode == KEYDB_SEARCH_MODE_FPR20
+                || desc.mode == KEYDB_SEARCH_MODE_FPR))
+        log_info (_("Warning: value '%s' for %s"
+                    " should be a long keyid or a fingerprint.\n"),
+                  t->d, option);
+
+      if (! hd)
+        hd = keydb_new ();
+      else
+        keydb_search_reset (hd);
+
+      err = keydb_search (hd, &desc, 1, NULL);
+      if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+        {
+          if (error_if_not_found)
+            {
+              if (! rc)
+                rc = err;
+
+              log_error (_("no such key corresponding to '%s'\n"), t->d);
+              if (!opt.quiet)
+                log_info (_("(check argument of option '%s')\n"), option);
+            }
+          continue;
+        }
+      if (err)
+        {
+          if (! rc)
+            rc = err;
+
+          log_error (_("error looking up '%s' in keyring: %s.\n"),
+                     t->d, gpg_strerror (err));
+          break;
+        }
+
+      err = keydb_get_keyblock (hd, &kb);
+      if (err)
+        {
+          if (! rc)
+            rc = err;
+
+          log_error (_("error reading key block for '%s': %s\n"),
+                     t->d, gpg_strerror (err));
+          continue;
+        }
+
+      pk = kb->pkt->pkt.public_key;
+      fingerprint_from_pk (pk, fingerprint_bin, &fingerprint_bin_len);
+      assert (fingerprint_bin_len == sizeof (fingerprint_bin));
+      bin2hex (fingerprint_bin, MAX_FINGERPRINT_LEN, fingerprint);
+      add_to_strlist (&s2, fingerprint);
+      s2->flags = s->flags;
+
+      release_kbnode (kb);
+
+      /* Continue the search.  */
+      err = keydb_search (hd, &desc, 1, NULL);
+      if (! (gpg_err_code (err) == GPG_ERR_NOT_FOUND
+             || gpg_err_code (err) == GPG_ERR_EOF))
+        {
+          char fingerprint_bin2[MAX_FINGERPRINT_LEN];
+          size_t fingerprint_bin2_len = sizeof (fingerprint_bin2);
+          char fingerprint2[2 * MAX_FINGERPRINT_LEN + 1];
+
+          log_error (_("Error: the key specification '%s' is ambiguous.\n"),
+                     t->d);
+          if (!opt.quiet)
+            log_info (_("(check argument of option '%s')\n"), option);
+
+          err = keydb_get_keyblock (hd, &kb);
+          if (err)
+            log_error (_("error reading key block for '%s': %s.\n"),
+                       t->d, gpg_strerror (err));
+          else
+            {
+              pk = kb->pkt->pkt.public_key;
+              fingerprint_from_pk (pk, fingerprint_bin2, &fingerprint_bin2_len);
+              assert (fingerprint_bin2_len == sizeof (fingerprint_bin2));
+              bin2hex (fingerprint_bin2, MAX_FINGERPRINT_LEN, fingerprint2);
+
+              log_error ("'%s' matches at least: %s and %s.\n",
+                         t->d, fingerprint, fingerprint2);
+
+              release_kbnode (kb);
+            }
+        }
+    }
+
+  strlist_rev (&s2);
+
+  if (hd)
+    keydb_release (hd);
+
+  free_strlist (s);
+  *sp = s2;
+  return rc;
+}
 
 int
 main (int argc, char **argv)
@@ -2582,7 +2735,8 @@ main (int argc, char **argv)
 
 #endif /*!NO_TRUST_MODELS*/
          case oDefaultKey:
-            add_to_strlist (&opt.def_secret_key, pargs.r.ret_str);
+            sl = add_to_strlist (&opt.def_secret_key, pargs.r.ret_str);
+            sl->flags = (pargs.r_opt << 2);
             break;
          case oDefRecipient:
             if( *pargs.r.ret_str )
@@ -2774,22 +2928,23 @@ main (int argc, char **argv)
          case oNoEncryptTo: opt.no_encrypt_to = 1; break;
          case oEncryptTo: /* store the recipient in the second list */
            sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
-           sl->flags = 1;
+           sl->flags = (pargs.r_opt << 2) | 1;
            break;
          case oHiddenEncryptTo: /* store the recipient in the second list */
            sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
-           sl->flags = 1|2;
+           sl->flags = (pargs.r_opt << 2) | 1|2;
            break;
           case oEncryptToDefaultKey:
             opt.encrypt_to_default_key = 1;
             break;
          case oRecipient: /* store the recipient */
-           add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
+           sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
+           sl->flags = (pargs.r_opt << 2);
             any_explicit_recipient = 1;
            break;
          case oHiddenRecipient: /* store the recipient with a flag */
            sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
-           sl->flags = 2;
+           sl->flags = (pargs.r_opt << 2) | 2;
             any_explicit_recipient = 1;
            break;
 
@@ -2832,7 +2987,8 @@ main (int argc, char **argv)
          case oAskCertLevel: opt.ask_cert_level = 1; break;
          case oNoAskCertLevel: opt.ask_cert_level = 0; break;
          case oLocalUser: /* store the local users */
-           add_to_strlist2( &locusr, pargs.r.ret_str, utf8_strings );
+           sl = add_to_strlist2( &locusr, pargs.r.ret_str, utf8_strings );
+            sl->flags = (pargs.r_opt << 2);
            break;
          case oCompress:
            /* this is the -z command line option */
@@ -3740,19 +3896,33 @@ main (int argc, char **argv)
         break;
       }
 
-    if (opt.encrypt_to_default_key)
-      {
-        const char *default_key = parse_def_secret_key (ctrl);
-        if (default_key)
-          {
-            sl = add_to_strlist2 (&remusr, default_key, utf8_strings);
-            sl->flags = 1;
-          }
-        else if (opt.def_secret_key)
-          log_info (_("--encrypt-to-default-key specified, but no valid default keys specified.\n"));
-        else
-          log_info (_("--encrypt-to-default-key specified, but --default-key not specified.\n"));
-      }
+    {
+      int have_def_secret_key = opt.def_secret_key != NULL;
+
+      rc = check_user_ids (&locusr, 1, 1);
+      if (rc)
+        g10_exit (1);
+      rc = check_user_ids (&remusr, 0, 1);
+      if (rc)
+        g10_exit (1);
+      rc = check_user_ids (&opt.def_secret_key, 1, 0);
+      if (rc)
+        g10_exit (1);
+
+      if (opt.encrypt_to_default_key)
+        {
+          const char *default_key = parse_def_secret_key (ctrl);
+          if (default_key)
+            {
+              sl = add_to_strlist2 (&remusr, default_key, utf8_strings);
+              sl->flags = (oEncryptToDefaultKey << 2) | 1;
+            }
+          else if (have_def_secret_key)
+            log_info (_("--encrypt-to-default-key specified, but no valid default keys specified.\n"));
+          else
+            log_info (_("--encrypt-to-default-key specified, but --default-key not specified.\n"));
+        }
+    }
 
     /* The command dispatcher.  */
     switch( cmd )