[PATCH GnuPG] RFC: g10: Improve and unify key selection.

Justus Winter justus at g10code.com
Mon Oct 31 13:12:16 CET 2016


* g10/getkey.c (get_best_pubkey_byname): New function.
* g10/keydb.h (get_best_pubkey_byname): New prototype.
* g10/keylist.c (locate_one): Use the new function.
* g10/pkclist.c (find_and_check_key): Likewise.
--

When a name resembling a mail address is given to either --locate-key
or --recipient, rank the search results and use only the most relevant
key.

This also lets us query which key will be used for encryption using
--locate-key.  XXX: Is this true?  B/c if we use --recipient, we only
consider keys usable for encryption.  Otoh, --locate-key has no such
constraint.

GnuPG-bug-id: 2359
Signed-off-by: Justus Winter <justus at g10code.com>
---
 g10/getkey.c  | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 g10/keydb.h   |   7 ++++
 g10/keylist.c |   2 +-
 g10/pkclist.c |   2 +-
 4 files changed, 118 insertions(+), 2 deletions(-)

diff --git a/g10/getkey.c b/g10/getkey.c
index 5ef5fc3..53b9156 100644
--- a/g10/getkey.c
+++ b/g10/getkey.c
@@ -1462,6 +1462,115 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk,
 }
 
 
+/* This function works like get_pubkey_byname, but if the name
+   resembles a mail address, the results are ranked and only the best
+   result is returned.  */
+int
+get_best_pubkey_byname (ctrl_t ctrl, GETKEY_CTX *retctx, PKT_public_key *pk,
+                        const char *name, KBNODE *ret_keyblock,
+                        int include_unusable, int no_akl)
+{
+  int rc;
+  struct getkey_ctx_s *ctx = NULL;
+
+  rc = get_pubkey_byname (ctrl, &ctx, pk, name, ret_keyblock,
+                          NULL, include_unusable, no_akl);
+  if (rc)
+    {
+      if (ctx)
+        getkey_end (ctx);
+      if (retctx)
+        *retctx = NULL;
+      return rc;
+    }
+
+  if (is_valid_mailbox (name))
+    {
+      /* Rank results and return only the most relevant key.  */
+      unsigned int best_validity = TRUST_UNKNOWN;
+      PKT_public_key k, best = { 0 };
+      while (getkey_next (ctx, &k, NULL) == 0)
+        {
+          unsigned int validity;
+          validity = get_validity (ctrl, &k, NULL, NULL, 0);
+
+          if (validity & (TRUST_FLAG_REVOKED
+                          | TRUST_FLAG_SUB_REVOKED
+                          | TRUST_FLAG_DISABLED)
+              || (validity & TRUST_MASK) == TRUST_EXPIRED)
+            continue;
+
+          if (validity > best_validity
+              || (validity == best_validity
+                  && k.timestamp > best.timestamp))
+            {
+              release_public_key_parts (&best);
+              best = k;
+              best_validity = validity;
+            }
+          else
+            release_public_key_parts (&k);
+        }
+      getkey_end (ctx);
+      ctx = NULL;
+
+      if (best.flags.valid)
+        {
+          if (retctx || ret_keyblock)
+            {
+              ctx = xtrycalloc (1, sizeof **retctx);
+              if (! ctx)
+                rc = gpg_error_from_syserror ();
+              else
+                {
+                  ctx->kr_handle = keydb_new ();
+                  if (! ctx->kr_handle)
+                    {
+                      xfree (ctx);
+                      *retctx = NULL;
+                      rc = gpg_error_from_syserror ();
+                    }
+                  else
+                    {
+                      u32 *keyid = pk_keyid (&best);
+                      ctx->exact = 1;
+                      ctx->nitems = 1;
+                      ctx->items[0].mode = KEYDB_SEARCH_MODE_LONG_KID;
+                      ctx->items[0].u.kid[0] = keyid[0];
+                      ctx->items[0].u.kid[1] = keyid[1];
+
+                      if (ret_keyblock)
+                        {
+                          release_kbnode (*ret_keyblock);
+                          *ret_keyblock = NULL;
+                          rc = getkey_next (ctx, NULL, ret_keyblock);
+                        }
+                    }
+                }
+            }
+
+          if (pk)
+            *pk = best;
+          else
+            release_public_key_parts (&best);
+        }
+    }
+
+  if (rc && ctx)
+    {
+      getkey_end (ctx);
+      ctx = NULL;
+    }
+
+  if (retctx && ctx)
+    *retctx = ctx;
+  else
+    getkey_end (ctx);
+
+  return rc;
+}
+
+
 /* Get a public key from a file.
  *
  * PK is the buffer to store the key.  The caller needs to make sure
diff --git a/g10/keydb.h b/g10/keydb.h
index 35512bb..11cc695 100644
--- a/g10/keydb.h
+++ b/g10/keydb.h
@@ -324,6 +324,13 @@ int get_pubkey_byname (ctrl_t ctrl,
                        KBNODE *ret_keyblock, KEYDB_HANDLE *ret_kdbhd,
 		       int include_unusable, int no_akl );
 
+/* Likewise, but only return the best match if NAME resembles a mail
+   address.  */
+int get_best_pubkey_byname (ctrl_t ctrl,
+			    GETKEY_CTX *retctx, PKT_public_key *pk,
+			    const char *name, KBNODE *ret_keyblock,
+			    int include_unusable, int no_akl);
+
 /* Get a public key directly from file FNAME.  */
 gpg_error_t get_pubkey_fromfile (ctrl_t ctrl,
                                  PKT_public_key *pk, const char *fname);
diff --git a/g10/keylist.c b/g10/keylist.c
index 212d77e..51dc409 100644
--- a/g10/keylist.c
+++ b/g10/keylist.c
@@ -650,7 +650,7 @@ locate_one (ctrl_t ctrl, strlist_t names)
 
   for (sl = names; sl; sl = sl->next)
     {
-      rc = get_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, NULL, 1, 0);
+      rc = get_best_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, 1, 0);
       if (rc)
 	{
 	  if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY)
diff --git a/g10/pkclist.c b/g10/pkclist.c
index da4cc06..eef3437 100644
--- a/g10/pkclist.c
+++ b/g10/pkclist.c
@@ -838,7 +838,7 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use,
   if (from_file)
     rc = get_pubkey_fromfile (ctrl, pk, name);
   else
-    rc = get_pubkey_byname (ctrl, NULL, pk, name, NULL, NULL, 0, 0);
+    rc = get_best_pubkey_byname (ctrl, NULL, pk, name, NULL, 0, 0);
   if (rc)
     {
       int code;
-- 
2.10.1




More information about the Gnupg-devel mailing list